2010-07-24 14 views
7

Soy nuevo en Ruby, y estoy teniendo un problema extraño con el método de inyección.Clase Nil al usar la inyección de Ruby

Cuando hago:

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

el resultado es 10, como se esperaba. Pero cuando lo haga

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

consigo un error:

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 

yo no entiendo muy bien por qué (presumiblemente) recuento es nulo en el segundo ejemplo, pero no el primero. En cualquier caso, ¿cómo voy a contar los evens de 1 a 10 usando Inject?

Respuesta

14

La expresión count + 1 if (x%2 == 0) devuelve cuando la condición no es verdadera, en la cual count se establece porque esa es la naturaleza del método de inyección.

Se podía solucionarlo volviendo count + 1 cuando es un número par y justo count cuando no lo es:

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

Una solución completamente diferente es usar select para seleccionar los números pares y utilizar el método Array#length para contar ellos.

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

Si está utilizando Rubí 1.8.7+, también se puede utilizar # Enumerable recuento, es decir, '(1..10) .count (Y:? Incluso)' –

+0

Cómo adorable !! - –

+0

¡Gracias! Eso tiene perfecto sentido ahora. En términos de eficiencia, ¿la inyección es mucho mejor, ya que no crea una matriz adicional? En este ejemplo, no importa mucho, pero ¿y si seleccionáramos miles de valores de un rango mucho más grande? –

3

Como yjerem ya se ha señalado, count + 1 if (x%2 == 0) serán evaluados para nil cuando x es impar. Y, aquí hay un problema: el valor nil se asignará a count, por lo que la siguiente iteración será nil + 1, lo que causó el error informado.

Es importante entender cómo inyectar obras (una copia de 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).

Una regla le mantendrá alejado de este tipo de error: el bloque siempre debe devolver el mismo tipo de valor como la de el acumulador. Si su ejemplo, el bloque devolverá un tipo de nil cuando x%2==0 si false.

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

Cuestiones relacionadas