Похоже, что .map(&singularize)
на самом деле не имеет отношения к вопросу, так что я его обрезал.
Пример данных
Давайте разберем этот фрагмент кода. Для этого нам нужны некоторые данные. Я предполагаю, что durations
может выглядеть примерно так, но фактическая структура, вероятно, не важна.
seconds = 1630
durations = [["Bob", 3600], ["Melba", 60]]
Боб потребляет время в часах; Мельба, в минутах.
Счетчик
Начнем с определения перечислителя.
enum = durations.each_with_object({})
#=> #<Enumerator: {"Bob"=>3600, "Melba"=>60}:each_with_object({})>
Первое значение, сгенерированное enum
Первый элемент генерируется enum
и передается в блок, а переменным блока присваиваются значения.
(name, duration), units = enum.next
#=> [["Bob", 3600], {}]
name
#=> "Bob"
duration
#=> 3600
units
#=> {}
Разложение массива
Процесс разбиения enum.next
называется декомпозиция массива . Эта ссылка заслуживает внимательного прочтения. На самом деле, весь файл хорошо написан и информативен. Обратите внимание, как круглые скобки в |(name, duration), units|
соответствуют скобкам в массиве duration
. См. Также Перечислитель # следующий .
Блок расчета
Теперь можно выполнить расчет блока.
units[name], seconds = seconds.divmod(duration)
#=> units["Bob"], seconds = 1630.divmod(3600)
#=> units["Bob"], seconds = [0, 1600]
units
#=> {"Bob"=>0}
seconds
#=> 1630
Еще раз, мы использовали декомпозицию массива. Поскольку duration
равняется количеству секунд в час для Боба (3600), units["Bob"]
устанавливается равным количеству часов, которые он «потребил», а seconds
теперь равно оставшемуся количеству оставшихся секунд. Поскольку последнее не изменяется (1630
), мы заключаем, что Боб может использовать время только в нескольких часах, поэтому он потратил ноль секунд.
Второе значение, сгенерированное enum
После того, как блок сообщит enum
, что он готов к созданию другого элемента, мы имеем следующее.
(name, duration), units = enum.next
#=> [["Melba", 60], {"Bob"=>1}]
name
#=> "Melba"
duration
#=> 60
units
#=> {"Bob"=>0}
Обратите внимание, что units
был обновлен. Продолжая,
units[name], seconds = seconds.divmod(duration)
#=> units["Melba"], seconds = 1630.divmod(60)
#=> units["Melba"], seconds = [27, 10]
units
#=> {"Bob"=>0, "Melba"=>27}
seconds
#=> 10
Мы видим, что Мельба потребляет 27
минут из 1630
секунд, оставляя 10
секунд.
Блок запрашивает следующий элемент из enum
Блок теперь готов для другого элемента из enum
.
(name, duration), units = enum.next
#=> #StopIteration (iteration reached an end)
enum
вызвал исключение StopInteration
, поскольку у него больше нет элементов для выдачи. Это приводит к тому, что блоком возвращается значение units
({"Bob"=>0, "Melba"=>27}
).
Удалите непотребителей времени
Наконец, мы избавляемся от всех, кто потратил ноль секунд.
{"Bob"=>0, "Melba"=>27}.reject { |k, v| v.zero? }
#=> {"Melba"=>27}
Более сложные примеры декомпозиции массива
Вот два.
arr = [[1, {a:2}, [3, [4, 5..6]]], 7]
(a, b, (c, (d, e))), f = arr
a #=> 1
b #=> {:a=>2}
c #=> 3
d #=> 4
e #=> 5..6
f #=> 7
[arr].each do |(a,b,(c,(d,e))),f|
puts a
puts b
puts c
puts d
puts e
puts f
end
1
{:a=>2}
3
4
5..6
7
Опять же, сравните расположение скобок с местами в скобках в arr
.
Использование декомпозиции массива чрезвычайно полезно, но, похоже, используется недостаточно.