Код
def doit(str)
str.each_char.each_slice(2).with_object(['','']) do |(c_even, c_odd), (s_even, s_odd)|
s_even << c_even
s_odd << c_odd unless c_odd.nil?
end.join(' ')
end
Примеры
doit "abracadabra"
#=> "arcdba baaar"
doit "Jack be nimble, Jack be quick"
#=> "Jc enml,Jc eqik akb ibe akb uc"
Объяснение
Для
str = "abracadabra"
enum0 = str.each_char
#=> #<Enumerator: "abracadabra":each_char>
Мы можем преобразовать нумератор enum0
в массив, чтобы увидеть, какие значения он будет генерировать:
enum0.to_a
#=> ["a", "b", "r", "a", "c", "a", "d", "a", "b", "r", "a"]
enum1 = enum0.each_slice(2)
#=> #<Enumerator: #<Enumerator: "abracadabra":each_char>:each_slice(2)>
enum1.to_a
#=> [["a", "b"], ["r", "a"], ["c", "a"], ["d", "a"], ["b", "r"], ["a"]]
enum2 = enum1.with_object(['',''])
#=> #<Enumerator: #<Enumerator: #<Enumerator: "abracadabra":each_char>:each_slice(2)>
# :with_object(["", ""])>
enum2.to_a
#=> [[["a", "b"], ["", ""]], [["r", "a"], ["", ""]], [["c", "a"], ["", ""]],
# [["d", "a"], ["", ""]], [["b", "r"], ["", ""]], [["a"], ["", ""]]]
Если вы исследуете Возврат значения, полученные при построении enum1
и enum2
, вы увидите, что их можно считать «составными» энтузиастами.
Первый элемент enum2
генерируется и передается в блок, присвоения значений четырех блоков переменных :
(c_even, c_odd), (s_even, s_odd) = enum2.next
#=> [["a", "b"], ["", ""]]
c_even #=> "a"
c_odd #=> "b"
s_even #=> ""
s_odd #=> ""
Расчет блока теперь выполняется.
s_even << c_even
#=> "a"
s_odd << c_odd unless c_odd.nil?
# s_odd << c_odd unless false
# s_odd << c_odd
#=> "b"
Возвращение значения "a"
и "b"
являются новые значения s_even
и s_odd
соответственно.
Теперь следующий элемент enum_2
генерируется, передается в блок и расчеты блока выполняются:
(c_even, c_odd), (s_even, s_odd) = enum2.next
#=> [["r", "a"], ["a", "b"]]
s_even << c_even
# "a" << "r"
#=> "ar"
s_odd << c_odd unless c_odd.nil?
# s_odd << c_odd unless "a".nil?
# s_odd << c_odd
#=> "ba"
Расчеты продолжают таким образом, пока последнее значение enum2
не генерируется: ["a"]
. Это приводит к назначению nil
на c_odd
, поэтому вторая строка блока не выполняется . Наконец, массив из двух строк соединен с разделительным пространством.
Другой способ
def doit(str)
str.each_char.with_index.with_object(' ') { |(c,i),s|
s.insert(i.even? ? s.index(' ') : s.size, c) }
end
doit "abracadabra"
#=> "arcdba baaar"
1 Следующее выражение использует параллельное назначение (иногда называемый множественного присваивания) и устранения неоднозначности (иногда упоминается как разложение) для присвоения значений переменные.
2 Вторые альтернативные варианты могут быть написаны s_odd << c_odd.to_s
или s_odd << c_odd || ''
.
Кроме того, это абсолютно не идиоматический рубин, я считаю, что он потерпит неудачу в строках с нечетным количеством букв. Кроме того, когда вы говорите «O (n)», стоит упомянуть, что в этом конкретном случае «n». – mudasobwa
@mudasobwa Это не подведет. Я тестировал его перед публикацией. Кроме того, если вы используете программное обеспечение, то все знают, что 'n' находится в o (n). Кроме того, только потому, что мы так привыкли к синтаксическому сахару рубина, давайте не будем забывать об основах. Если это делается с помощью 'map', затем' join', а затем другой операции, такой как раздел, тогда я теряю циклы. Подумайте о таких операциях на большой строке, скажем, 10^10, ваше решение займет много времени, чтобы рассчитать ответ. – Surya
«скажем, 10^10», - набрал в консоли? Код должен решить проблему, а не пытаться быть швейцарским ножом. Использование сложных алгоритмов здесь является прекрасным примером преждевременной оптимизации, приводящей к нечитаемому и, как правило, плохому коду с запахом. – mudasobwa