Ответ 1
Просто потому, что Ruby динамически и неявно напечатан, не означает, что вам не нужно думать о типах.
Тип Enumerable#inject
без явного аккумулятора (обычно это называется reduce
) - это что-то вроде
reduce :: [a] → (a → a → a) → a
или в более рубиновой нотации, которую я только что составил
Enumerable[A]#inject {|A, A| A } → A
Вы заметите, что все типы одинаковы. Тип элемента Enumerable
, два типа аргументов блока, тип возврата блока и тип возвращаемого значения общего метода.
Тип Enumerable#inject
с явным аккумулятором (обычно это называется fold
) - это что-то вроде
fold :: [b] → a → (a → b → a) → a
или
Enumerable[B]#inject(A) {|A, B| A } → A
Здесь вы видите, что аккумулятор может иметь другой тип, чем тип элемента коллекции.
Эти два правила обычно передают вам все проблемы, связанные с Enumerable#inject
:
- тип аккумулятора и тип возврата блока должны быть одинаковыми
- когда не передается явный накопитель, тип аккумулятора совпадает с типом элемента
В этом случае это правило № 1, которое вас укусит. Когда вы делаете что-то вроде
acc[key] = value
в вашем блоке, присваивания оцениваются назначенным значением, а не получателем задания. Вам придется заменить это на
acc.tap { acc[key] = value }
См. также Почему метод инъекции Ruby не может суммировать длины строк без начального значения?
BTW: вы можете использовать деструктурирующее связывание, чтобы сделать ваш код более читаемым:
a.inject({}) {|r, (key, value)| r[key] = value; r }