Какова функция запятой в этом куске кода? - PullRequest
3 голосов
/ 07 апреля 2019

Я пытаюсь понять этот код методом method.each_with_object.Я не понимаю, как запятая (после units[name]) работает внутри блока.

Понятнее, какова его функция?Почему есть запятая, отделяющая units[name] от seconds=... и не записываемая как units[name, seconds=seconds....]?

Это фрагмент кода:

units = durations.each_with_object({}) do |(name, duration), units|
    units[name], seconds = seconds.divmod(duration)
end.reject { |k, v| v.zero? }.map(&singularize)

Ответы [ 2 ]

4 голосов
/ 07 апреля 2019

В Ruby вы можете «деконструировать» массив и связать его с несколькими переменными следующим образом a, b = [1, 2], где 1 будет связано с a и 2 с b. Для примера:

[1] pry(main)> a, b = [1, 2]
=> [1, 2]
[2] pry(main)> a
=> 1
[3] pry(main)> b
=> 2

Зная это, код выше совпадает с

units = durations.each_with_object({}) do |(name, duration), units|
    result = seconds.divmod(duration)
    units[name] = result.first
    seconds = result.last
    result
end.reject { |k, v| v.zero? }.map(&singularize)

Для размышления над вашим вопросом units[name, seconds=seconds....] не является допустимым Ruby. Хэш-метод [] ожидает только один аргумент. Вы можете определить свой собственный класс, который принимает больше.

# The following was tested on ruby 2.6

class A
  def [](name, surname)
    puts "#{name} #{surname}"
  end

  def []=(name, surname, value)
    puts "#{name} #{surname} = #{value}"
  end
end

a = A.new
a['foo', 'bar'] # prints 'foo bar'
a['foo', 'bar'] = 'baz' # prints 'foo bar = bar'

Таким образом, вы можете определить свою собственную реализацию Hash, которая делает то, что вы описали, но по умолчанию это не так.

0 голосов
/ 08 апреля 2019

Похоже, что .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.

Использование декомпозиции массива чрезвычайно полезно, но, похоже, используется недостаточно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...