2009-04-02 9 views
92
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10 

Estoy viendo este código pero mi cerebro no está registrando cómo el número 10 puede convertirse en el resultado. ¿Alguien le importaría explicar lo que está sucediendo aquí?Necesita una explicación simple del método de inyección

+1

Ver [Wikipedia: Fold (función de orden superior)] (http://en.wikipedia.org/wiki/Fold_%28higher-order_function%29): inyectar es un "doblez a la izquierda", aunque (desafortunadamente) a menudo con efectos secundarios en el uso de Ruby. – user2864740

Respuesta

146

Puede pensar en el primer argumento de bloque como un acumulador: el resultado de cada ejecución del bloque se almacena en el acumulador y luego pasa a la siguiente ejecución del bloque. En el caso del código que se muestra arriba, está incumpliendo el acumulador, resultado, a 0. Cada ejecución del bloque agrega el número dado al total actual y luego almacena el resultado en el acumulador. La próxima llamada al bloque tiene este nuevo valor, lo agrega, lo almacena de nuevo y lo repite.

Al final del proceso, se inyectan los rendimientos del acumulador, que en este caso es la suma de todos los valores de la matriz, o 10.

Aquí hay otro ejemplo simple de crear un hash de una matriz de objetos, cerrado por su representación de cadena:

[1,"a",Object.new,:hi].inject({}) do |hash, item| 
    hash[item.to_s] = item 
    hash 
end 

En este caso, son el impago nuestra acumulador a un hash vacío, entonces poblar cada vez que el bloque se ejecuta. Tenga en cuenta que debemos devolver el hash como la última línea del bloque, porque el resultado del bloque se almacenará nuevamente en el acumulador.

+1

Gracias por la gran explicación y el ejemplo de hash, eso realmente lo dejó en claro. –

+0

gran explicación, sin embargo, en el ejemplo dado por el OP, lo que se devuelve (como el hash en su ejemplo). Termina con resultado + explicación y debe tener un valor de retorno, ¿sí? – Projjol

+0

@Projjol el 'resultado + explicación' es tanto la transformación al acumulador como el valor de retorno. Es la última línea del bloque, lo que lo convierte en un retorno implícito. –

18

Los itera código a través de los cuatro elementos dentro de la matriz y añade el resultado anterior para el elemento actual:

  • 1 + 2 = 3
  • 3 + 3 = 6
  • 6 + 4 = 10
6

Inject aplica el bloque

result + element 

a cada elemento en la matriz. Para el siguiente elemento ("elemento"), el valor devuelto por el bloque es "resultado". La forma en que lo ha llamado (con un parámetro), "resultado" comienza con el valor de ese parámetro. Entonces el efecto es agregar los elementos.

67

inject toma un valor para comenzar (el 0 en su ejemplo), y un bloque, y ejecuta ese bloque una vez para cada elemento de la lista.

  1. En la primera iteración, que pasa en el valor proporcionado como valor inicial, y el primer elemento de la lista, y guarda el valor que devuelve su bloque (en este caso result + element).
  2. Luego ejecuta nuevamente el bloque, pasando el resultado de la primera iteración como primer argumento, y el segundo elemento de la lista como segundo argumento, guardando nuevamente el resultado.
  3. Continúa de esta manera hasta que haya consumido todos los elementos de la lista.

La manera más fácil de explicar esto puede ser mostrar cómo funciona cada paso, para su ejemplo; se trata de un conjunto imaginario de pasos que muestra cómo este resultado podría ser evaluado:

[1, 2, 3, 4].inject(0) { |result, element| result + element } 
[2, 3, 4].inject(0 + 1) { |result, element| result + element } 
[3, 4].inject((0 + 1) + 2) { |result, element| result + element } 
[4].inject(((0 + 1) + 2) + 3) { |result, element| result + element } 
[].inject((((0 + 1) + 2) + 3) + 4) { |result, element| result + element } 
(((0 + 1) + 2) + 3) + 4 
10 
+0

Gracias por escribir los pasos. Esto ayudó mucho. Aunque estaba un poco confundido acerca de si quiere decir que el siguiente diagrama es cómo el método de inyección se implementa debajo en términos de lo que se pasa como argumentos para inyectar. –

+2

El siguiente diagrama se basa en cómo * podría * implementarse; no necesariamente se implementa exactamente de esta manera. Es por eso que dije que es un conjunto imaginario de pasos; demuestra la estructura básica, pero no la implementación exacta. –

+0

La mejor expresión visual de inyectar que he visto en mi vida. – scaryguy

13

lo que decían, pero tenga en cuenta también que no siempre es necesario proporcionar un "valor inicial":

[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10 

es lo mismo que

[1, 2, 3, 4].inject { |result, element| result + element } # => 10 

Pruébalo, esperaré.

Cuando no se pasa ningún argumento para inyectar, los primeros dos elementos se pasan a la primera iteración. En el ejemplo anterior, el resultado es 1 y el elemento es 2 la primera vez, por lo que se realiza una llamada menos al bloque.

2

Este código no permite la posibilidad de no pasar un valor de inicio, pero puede ayudar a explicar lo que está sucediendo.

def incomplete_inject(enumerable, result) 
    enumerable.each do |item| 
    result = yield(result, item) 
    end 
    result 
end 

incomplete_inject([1,2,3,4], 0) {|result, item| result + item} # => 10 
11

El número que poner dentro de su() de inyectar representa un punto de partida, podría ser 0 ó 1000. el interior de las tuberías que tiene dos marcadores de posición | x, y |. x = el número que tengas dentro de .inject ('x'), y el segundo representa cada iteración de tu objeto.

[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15

1 + 5 = 6 2 + 6 = 8 3 + 8 = 11 11 + 4 = 15

-1

es lo mismo que esto:

[1,2,3,4].inject(:+) 
=> 10 
+0

Si bien esto es real, no responde la pregunta. –

4
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10 

es equivalente a la siguiente:

def my_function(r, e) 
    r+e 
end 

a = [1, 2, 3, 4] 
result = 0 

a.each do |value| 
    result = my_function(result, value) 
end 
5

tldr;inject difiere de map de una manera importante: inject devuelve el valor de la última ejecución del bloque, mientras que map devuelve la matriz iterada.

Más que eso el valor de cada ejecución del bloque pasado a la siguiente ejecución a través del primer parámetro (result en este caso) y se puede inicializar ese valor (la parte (0)).

Su ejemplo anterior podría escribirse usando map así:

result = 0 # initialize result 
[1, 2, 3, 4].map { |element| result += element } 
# result => 10 

mismo efecto, pero inject es más concisa aquí.

A menudo encontrará que una asignación ocurre en el bloque map, mientras que una evaluación ocurre en el bloque inject.

El método que elija depende del alcance que desee para result. Cuando a no utilizan sería algo como esto:

result = [1, 2, 3, 4].inject(0) { |x, element| x + element } 

Usted podría ser como todo, "me Lookie, acabo combinado que todos en una sola línea", pero que la memoria también asignado temporalmente para x como arañazo variable que no era necesaria porque ya tenías result para trabajar.

3

[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10

En la llanura Inglés, que están pasando por (la iteración) a través de esta matriz ([1,2,3,4]). Usted iterará a través de esta matriz 4 veces, porque hay 4 elementos (1, 2, 3 y 4). El método de inyección tiene 1 argumento (el número 0), y agregará ese argumento al primer elemento (0 + 1. Esto equivale a 1). 1 se guarda en el "resultado". Luego, agrega ese resultado (que es 1) al siguiente elemento (1 + 2. Esto es 3). Esto ahora se guardará como resultado. Continúe: 3 + 3 es igual a 6. Y por último, 6 + 4 es igual a 10.

13

La sintaxis del método de inyección es el siguiente:

inject (value_initial) { |result_memo, object| block }

Vamos a resolver el ejemplo anterior, es decir

[1, 2, 3, 4].inject(0) { |result, element| result + element }

que da como salida.

Por lo tanto, antes de empezar vamos a ver cuáles son los valores almacenados en cada uno variables:

resultado = 0 El cero vino de inyección (valor) que es 0

elemento = 1 Es el primer elemento de la matriz.

Okey !!! Por lo tanto, vamos a empezar a entender el ejemplo anterior

Paso: 1 [1, 2, 3, 4].inject(0) { |0, 1| 0 + 1 }

Paso: 2 [1, 2, 3, 4].inject(0) { |1, 2| 1 + 2 }

Paso: 3 [1, 2, 3, 4].inject(0) { |3, 3| 3 + 3 }

Paso: 4 [1, 2, 3, 4].inject(0) { |6, 4| 6 + 4 }

Paso: 5 [1, 2, 3, 4].inject(0) { |10, Now no elements left in the array, so it'll return 10 from this step| }

Aquí Bold-Italic Los valores son elementos de la matriz y los valores Bold son los valores resultantes.

Espero que entienda el funcionamiento del método #inject del #ruby.

+1

¡Esta es una muy buena respuesta! –

0

Comience aquí y luego revise todos los métodos que toman bloques. http://ruby-doc.org/core-2.3.3/Enumerable.html#method-i-inject

¿Es el bloque lo que lo confunde o por qué tiene un valor en el método? Buena pregunta sin embargo. ¿Cuál es el método del operador allí?

result.+ 

¿Cómo se inicia?

#inject(0) 

¿Se puede hacer esto?

[1, 2, 3, 4].inject(0) { |result, element| result.+ element } 

¿Funciona?

[1, 2, 3, 4].inject() { |result = 0, element| result.+ element } 

ves que estoy partiendo de la idea de que simplemente resume todos los elementos de la matriz y se obtiene un número en la nota que aparece en la documentación.

Siempre se puede hacer esto

[1, 2, 3, 4].each { |element| p element } 

para ver la enumerable de la matriz conseguir itera a través. Esa es la idea básica.

Es solo que inyectar o reducir le da una nota o un acumulador que se envía.

Podríamos tratar de obtener un resultado

[1, 2, 3, 4].each { |result = 0, element| result + element } 

pero nada vuelve por lo que este solo actúa igual que antes

[1, 2, 3, 4].each { |result = 0, element| p result + element } 

en el bloque elemento de inspector.

0

Hay otra forma .inject método() de que es muy útil [4,5] .inject (&: +) que se suman todos los elementos de la zona de

0

Es sólo reduce o fold , si estás familiarizado con otros idiomas.

Cuestiones relacionadas