2010-03-22 4 views
9

ich eine Rails-Anwendung bin die Schaffung und haben diesen Code in einem meiner MethodenWarum muss ich .inject (0) anstelle von .inject verwenden, damit dies funktioniert?

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

item_numbers ist ein Array von Objekten aus meiner item_numbers Tabelle. Die Methode .amount, die ich auf sie anwende, sucht den Wert einer Elementnummer in einer separaten Tabelle und gibt sie als BigDecimal-Objekt zurück. Offensichtlich fügt die inject-Methode dann alle zurückgegebenen i.amount-Objekte hinzu und das funktioniert einwandfrei.

Ich bin nur neugierig, warum es nicht funktioniert, wenn ich als

diese Aussage schrieb
item_numbers.inject {|sum, i| sum + i.amount} 

Nach meinem treuen pickaxe Buch diese gleichwertig betrachtet werden sollte. Liegt es daran, dass i.amount ein BigDecimal ist? Wenn ja, warum funktioniert es jetzt? Wenn nicht, warum funktioniert es dann nicht?

Antwort

14

Was wir in API lesen können.

Wenn Sie einenicht explizit angebenInitialwert für Memo, dann verwendet das erste Element der Sammlung wird als der Anfangswert von Memo verwendet.

So wird item_numbers [0] als Anfangswert angegeben - aber es ist keine Zahl, es ist ein Objekt. So haben wir einen Fehler

undefined Methode `+ '.

So haben wir Anfangswert als 0

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

+0

Sie können tun, einen kleinen Test (10..15) .inject tun | Summe, i | p Summe Summe + i Ende es wird zurück: 10, 21, 33, 46, 60 => 75 Wie Sie _sum_ bekommt erstes Element von Array als Anfangswert – fl00r

+0

Das erklärt es schön zu sehen. Vielen Dank. – brad

7

Es ist, weil Sie auf i.amount im Gegensatz zu nur einfach i zugreifen. In der Version, die nicht funktioniert, machen Sie implizit item_numbers[0] + item_numbers[1].amount + ....

Eine Abkürzung wäre item_numbers.map(&:amount).inject(&:+), aber dieser Weg kann zu zwei Iterationen über die Liste führen, wenn map keinen Enumerator zurückgibt.

Wenn das hast du nicht überzeugen, schauen, was ausgedruckt wird, wenn wir eine Methode amount auf Fixnum definieren, die den Wert druckt, bevor er zurückkehrt:

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> 

Wir können deutlich sehen, dass amount nicht aufgerufen auf dem ersten Element, wenn ein Startwert wird in nicht explizit übergeben

+0

+1 für klare exemple, die das Verhalten perfekt illustriert – Jean