Почему я должен использовать .inject (0), а не .inject, чтобы это работало? - PullRequest
11 голосов
/ 22 марта 2010

Я создаю приложение rails и использовал этот код в одном из моих методов

item_numbers.inject(0) {|sum, i| sum + i.amount}

item_numbers - это массив объектов из моей таблицы item_numbers. Применяемый к ним метод .amount ищет значение item_number в отдельной таблице и возвращает его как объект BigDecimal. Очевидно, что метод inject добавляет все возвращенные объекты i.amount, и это прекрасно работает.

Мне просто любопытно, почему это не сработало, когда я написал это утверждение как

item_numbers.inject {|sum, i| sum + i.amount}

Согласно моей верной книге кирки, они должны быть эквивалентны. Это потому, что i.amount является BigDecimal? Если так, то почему это сейчас работает? Если нет, то почему это не работает.

Ответы [ 2 ]

17 голосов
/ 22 марта 2010

Что мы можем прочитать в API:

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

Таким образом, item_numbers [0] будет указан как начальное значение - но это не число, это объект. Итак, мы получили ошибку

неопределенный метод `+ '.

Таким образом, мы должны указать начальное значение как 0

item_numbers.inject (0) {| sum, i | сумма + я}

7 голосов
/ 22 марта 2010

Это потому, что вы получаете доступ к i.amount, а не просто i.В версии, которая не работает, вы неявно выполняете item_numbers[0] + item_numbers[1].amount + ....

Один сокращенный вариант будет item_numbers.map(&:amount).inject(&:+), но этот путь может привести к двум итерациям по списку, если map не делает 't вернуть перечислитель.

Если это не убедило вас, посмотрите, что будет напечатано, если мы определим метод amount в Fixnum, который печатает значение перед его возвратом:

irb(main):002:1>   def amount
irb(main):003:2>     puts "My amount is: #{self}"
irb(main):004:2>     return self
irb(main):005:2>   end
irb(main):006:1> end
=> nil
irb(main):007:0> [1,2,3].inject { |sum, i| sum + i.amount }
My amount is: 2
My amount is: 3
=> 6
irb(main):008:0> [1,2,3].inject(0) { |sum, i| sum + i.amount }
My amount is: 1
My amount is: 2
My amount is: 3
=> 6
irb(main):009:0>

Мы можем ясно видеть, что amount не вызывается для первого элемента, когда начальное значение явно не передается.

...