2010-07-24 15 views
7

Jestem nowy dla Ruby i mam dziwny problem z metodą wstrzykiwania.Klasa zerowa przy użyciu wtrysku Ruby

Kiedy zrobić:

(1..10).inject(0) {|count,x| count + 1} 

wynik wynosi 10, zgodnie z oczekiwaniami. Ale kiedy zrobić

(1..10).inject(0) {|count,x| count + 1 if (x%2 == 0)} 

pojawia się błąd:

NoMethodError: undefined method `+' for nil:NilClass 
    from (irb):43 
    from (irb):43:in `inject' 
    from (irb):43:in `each' 
    from (irb):43:in `inject' 
    from (irb):43 

ja naprawdę nie rozumiem dlaczego (prawdopodobnie) licznik jest zerowa w drugim przykładzie, ale nie pierwszy. W każdym razie, w jaki sposób mogę liczyć równości od 1 do 10 za pomocą wstrzyknięcia?

Odpowiedz

14

Wyrażenie count + 1 if (x%2 == 0) zwraca nil, gdy warunek nie jest spełniony, co zostanie ustawione na count, ponieważ taka jest natura metody wstrzykiwania.

Można go naprawić wracając count + 1 gdy jest liczbą parzystą i tylko count gdy nie ma:

(1..10).inject(0) { |count,x| x % 2 == 0 ? count + 1 : count } 

Zupełnie inny rozwiązaniem jest użycie select wybrać parzyste i użyć metody Array#length liczyć im.

(1..10).select { |x| x % 2 == 0 }.length 
+1

Jeśli używasz Ruby 1.8.7+, można również użyć Enumerable count #, to znaczy '(1..10) .count (&:? Nawet)' –

+0

Jak urocze !! - –

+0

Dzięki! Teraz ma to sens. Jeśli chodzi o efektywność, czy sposób wstrzykiwania jest lepszy, ponieważ nie powoduje on powstawania dodatkowej matrycy? W tym przykładzie nie ma to większego znaczenia, ale co by było, gdybyśmy wybrali 1000 wartości z dużo większego zakresu? –

3

Jak yjerem już wspomniano, count + 1 if (x%2 == 0) będą oceniane na nil gdy x jest nieparzyste. I tutaj jest problem: wartość nil zostanie przypisana do count, więc następna iteracja będzie nil + 1, co spowodowało zgłoszenie błędu.

Ważne jest, aby zrozumieć, jak wstrzykiwać prace (kopia z ruby-doc)

enum.inject(initial) {| memo, obj | block } => obj

enum.inject {| memo, obj | block } => obj

Combines the elements of enum by applying the block to an accumulator value (memo) and each element in turn. At each step, memo is set to the value returned by the block. The first form lets you supply an initial value for memo. The second form uses the first element of the collection as a the initial value (and skips that element while iterating).

Reguła będzie utrzymać się z dala od tego rodzaju błędu: blok powinien zawsze zwraca ten sam typ wartości, jak w akumulator. Jeśli twój przykład, blok zwróci typ nil, gdy x%2==0, jeśli .

(1..10).inject(0) {|count,x| count + 1 if (x%2 == 0)}

Powiązane problemy