Как уже сообщается в сообщении об ошибке, проблема в том, что у вас есть TypeError
.Тот факт, что Ruby динамически и неявно типизирован, не означает, что вам не нужно думать о типах.
Тип Enumerable#inject
без явного аккумулятора (обычно это называется reduce
) - это нечтокак
reduce :: [a] → (a → a → a) → a
или в более рубиновой записи, которую я только что составил
Enumerable[A]#inject {|A, A| A } → A
Вы заметите, что все типы одинаковы.Тип элемента Enumerable
, два типа аргумента блока, тип возвращаемого значения блока и тип возвращаемого значения общего метода.
В вашем случае типы для блока просто не задаютсят сложить.Блок проходит два String
с, и он должен вернуть String
.Но вы вызываете метод +
для первого аргумента (который является String
) с аргументом, который является Integer
.Но String#+
не требует Integer
, требуется только String
или, точнее, что-то, что может быть преобразовано в String
, то есть что-то, что отвечает #to_str
.Вот почему вы получаете TypeError
для String#+
.
Тип 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 }
В вашем конкретном случае два решения уже были упомянуты.Либо используйте явный аккумулятор
ary.reduce(0){|acc, e| acc + e.length }
или конвертируйте в целые числа first
ary.map(&:length).reduce(:+)