Почему #each_with_object и #inject меняют порядок параметров блока?

#each_with_object и #inject можно использовать для создания хэша.

Например:

matrix = [['foo', 'bar'], ['cat', 'dog']]

some_hash = matrix.inject({}) do |memo, arr|
  memo[arr[0]] = arr
  memo # no implicit conversion of String into Integer (TypeError) if commented out
end
p some_hash # {"foo"=>["foo", "bar"], "cat"=>["cat", "dog"]}

another_hash = matrix.each_with_object({}) do |arr, memo|
  memo[arr[0]] = arr
end
p another_hash # {"foo"=>["foo", "bar"], "cat"=>["cat", "dog"]}

Одно из ключевых различий между ними заключается в том, что #each_with_object отслеживает memo на протяжении всей итерации, а #inject устанавливает memo равным значению, возвращаемому блоком на каждой итерации.

Другим отличием является порядок или параметры блока.

Есть ли какое-то намерение, о котором здесь сообщается? Не имеет смысла менять местами параметры блоков двух похожих методов.


person mbigras    schedule 21.01.2017    source источник


Ответы (1)


У них другая родословная.

  • each_with_object был добавлен в Ruby 1.9 в 2007 году.
  • inject восходит к Smalltalk в 1980 году

Я предполагаю, что если бы язык был разработан с использованием обоих методов с самого начала, они, вероятно, ожидали бы аргументы в одном и том же порядке. Но это не так. inject существует с самого начала Ruby, тогда как each_with_object был добавлен только 10 лет спустя.

inject ожидает аргументы в том же порядке, что и inject:into: в Smalltalk.

collection inject: 0 into: [ :memo :each | memo + each ]

который делает левую складку. Вы можете представить коллекцию как длинную полоску бумаги, сложенную слева вверх, и скользящее окно функции сгиба всегда представляет собой уже сложенную часть плюс следующий элемент оставшейся бумажной полоски.

# (memo = 0 and 1), 2, 3, 4  
# (memo = 1 and 2), 3, 4                 
# (memo = 3 and 3), 4                    
# (memo = 6 and 4)                      

Следование соглашению о Smalltalk тогда имело смысл, так как все начальные методы в Enumerable взяты из Smalltalk, и Матц не хотел путать людей, знакомых с Smalltalk.

Никто не мог предугадать, что произойдет в 2007 году, когда each_with_object было введено в Ruby 1.9, а порядок аргументов отражал лексический порядок имени метода, то есть each ... object.

И, следовательно, эти два метода предполагают аргументы в разном порядке по историческим причинам.

person akuhn    schedule 21.01.2017
comment
Хороший и интересный аккаунт. Я ожидаю, что также было желание соответствовать Enumerator. #с_объектом. Например. arr.each_with_index.with_object ({}) { |(e,i),h|.... Наличие блочных переменных |h,(e,i)| было бы крайне запутанным. - person Cary Swoveland; 21.01.2017
comment
Согласованность (в размещении блочных переменных) с Enumerable# each_with_index и Enumerator#each_index Возможно, это тоже было фактором. - person Cary Swoveland; 22.01.2017
comment
порядок аргументов отражает лексический порядок имени метода, то есть each ... object............ Я всегда забываю порядок, теперь никогда не забуду. - person Sagar Pandya; 28.01.2017
comment
Я думаю, стоит отметить, что each_with_object и inject принципиально различаются: each_with_object не волнует, что дает блок, inject очень заботится (потому что значение блока передается обратно в блок, поэтому inject похоже на block(block(block(...), eX), eY)). - person mu is too short; 25.04.2018