Necesita una explicación simple del método de inyección
142
[1,2,3,4].inject(0){|result, element| result + element }# => 10
Estoy mirando este código, pero mi cerebro no está registrando cómo el número 10 puede convertirse en el resultado. ¿A alguien le importaría explicar lo que está pasando aquí?
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 se pasa a la siguiente ejecución del bloque. En el caso del código que se muestra arriba, está omitiendo el acumulador, resultado, a 0. Cada ejecución del bloque agrega el número dado al total actual y luego almacena el resultado nuevamente en el acumulador. La siguiente llamada de bloque tiene este nuevo valor, se agrega, lo almacena nuevamente y se repite.
Al final del proceso, inject devuelve el acumulador, que en este caso es la suma de todos los valores de la matriz, o 10.
Aquí hay otro ejemplo simple para crear un hash a partir de una matriz de objetos, clave por su representación de cadena:
En este caso, estamos predeterminando nuestro acumulador a un hash vacío, y luego lo llenamos cada vez que se ejecuta el bloque. 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.
Gran explicación, sin embargo, en el ejemplo dado por el OP, lo que se devuelve (como el hash está en su ejemplo). Termina con resultado + explicación y debería tener un valor de retorno, ¿sí?
Projjol
1
@Projjol the result + explanationes tanto la transformación al acumulador como el valor de retorno. Es la última línea del bloque, por lo que es un retorno implícito.
KA01
87
injecttoma un valor para comenzar (el 0en su ejemplo) y un bloque, y ejecuta ese bloque una vez para cada elemento de la lista.
En la primera iteración, pasa el valor que proporcionó como valor inicial y el primer elemento de la lista, y guarda el valor que devolvió su bloque (en este caso result + element).
Luego ejecuta el bloque nuevamente, pasando el resultado de la primera iteración como el primer argumento, y el segundo elemento de la lista como el segundo argumento, guardando nuevamente el resultado.
Continúa de esta manera hasta que haya consumido todos los elementos de la lista.
La forma más fácil de explicar esto puede ser mostrar cómo funciona cada paso, por ejemplo; Este es un conjunto imaginario de pasos que muestran cómo se puede evaluar este resultado:
[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)+410
Gracias por escribir los pasos. Esto ayudó mucho. Aunque estaba un poco confundido acerca de si quieres decir que el diagrama a continuación es cómo se implementa el método de inyección debajo en términos de lo que se pasa como argumentos para inyectar.
2
El siguiente diagrama se basa en cómo se podría implementar; no necesariamente se implementa exactamente de esta manera. Por eso dije que es un conjunto imaginario de pasos; demuestra la estructura básica, pero no la implementación exacta.
Brian Campbell el
27
La sintaxis para el método de inyección es la siguiente:
Lo que dijeron, pero tenga en cuenta también que no siempre necesita 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, te espero.
Cuando no se pasa ningún argumento para inyectar, los dos primeros 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.
El número que coloca dentro de su () de inyección representa un lugar inicial, podría ser 0 o 1000. Dentro de las tuberías tiene dos marcadores de posición | x, y |. x = cualquier número que tengas dentro del .inject ('x'), y el segundo representa cada iteración de tu objeto.
[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15
a cada elemento de la matriz. Para el siguiente elemento ("elemento"), el valor devuelto por el bloque es "resultado". Como lo ha llamado (con un parámetro), "resultado" comienza con el valor de ese parámetro. Entonces el efecto es sumar los elementos.
tldr; injectdifiere de mapuna manera importante: injectdevuelve el valor de la última ejecución del bloque, mientras que mapdevuelve la matriz sobre la que iteró.
Más que eso, el valor de cada ejecución de bloque pasó a la siguiente ejecución a través del primer parámetro ( resulten este caso) y puede inicializar ese valor (la (0)parte).
Su ejemplo anterior podría escribirse mapasí:
result =0# initialize result[1,2,3,4].map {|element| result += element }# result => 10
Mismo efecto pero injectes más conciso aquí.
A menudo encontrará que una tarea ocurre en el mapbloque, mientras que una evaluación ocurre en el injectbloque.
El método que elija depende del alcance que desee result. Cuándo no usarlo sería algo como esto:
result =[1,2,3,4].inject(0){|x, element| x + element }
Puede decir: "Mírame, acabo de combinar todo en una sola línea", pero también asignaste temporalmente la memoria xcomo una variable temporal que no era necesaria ya que ya tenías resultque trabajar.
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
En inglés simple, está pasando (iterando) a través de esta matriz ( [1,2,3,4]). Recorrerá 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úa: 3 + 3 es igual a 6. Y finalmente, 6 + 4 es igual a 10.
Este código no permite la posibilidad de no pasar un valor inicial, 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
¿Es el bloque lo que te confunde o por qué tienes un valor en el método? Buena pregunta sin embargo. ¿Cuál es el método del operador allí?
result.+
¿Cómo comienza?
#inject(0)
¿Podemos hacer esto?
[1,2,3,4].inject(0){|result, element| result.+ element }
¿Esto funciona?
[1,2,3,4].inject(){|result =0, element| result.+ element }
Verá, estoy construyendo sobre la idea de que simplemente suma todos los elementos de la matriz y produce un número en la nota que ve en los documentos.
Siempre puedes hacer esto
[1,2,3,4].each {|element| p element }
para ver cómo se puede iterar el enumerable de la matriz. 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 intentar obtener un resultado
[1,2,3,4].each {|result =0, element| result + element }
pero nada regresa, así que esto actúa igual que antes
[1,2,3,4].each {|result =0, element| p result + element }
Esta es una explicación simple y bastante fácil de entender:
Olvídate del "valor inicial", ya que es algo confuso al principio.
>[1,2,3,4].inject{|a,b| a+b}=>10
Puede entender lo anterior como: Estoy inyectando una "máquina sumadora" entre 1,2,3,4. Es decir, es 1 ♫ 2 ♫ 3 ♫ 4 y ♫ es una máquina sumadora, por lo que es lo mismo que 1 + 2 + 3 + 4, y es 10.
De hecho, puede inyectar un +entre ellos:
>[1,2,3,4].inject(:+)=>10
y es como, inyectar un +entre 1,2,3,4, haciéndolo 1 + 2 + 3 + 4 y es 10. Esta :+es la forma de Ruby de especificar +en forma de un símbolo.
Esto es bastante fácil de entender e intuitivo. Y si desea analizar cómo funciona paso a paso, es como: tomar 1 y 2, y ahora agregarlos, y cuando tenga un resultado, guárdelo primero (que es 3), y ahora, el siguiente es el almacenado valor 3 y el elemento de matriz 3 que pasa por el proceso a + b, que es 6, y ahora almacena este valor, y ahora 6 y 4 pasan por el proceso a + b, y es 10. Usted esencialmente está haciendo
((1+2)+3)+4
y es 10. El "valor inicial" 0es solo una "base" para empezar. En muchos casos, no lo necesitas. Imagínese si necesita 1 * 2 * 3 * 4 y es
[1,2,3,4].inject(:*)=>24
y ya está hecho. No necesita un "valor inicial" de 1para multiplicar todo 1.
Respuestas:
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 se pasa a la siguiente ejecución del bloque. En el caso del código que se muestra arriba, está omitiendo el acumulador, resultado, a 0. Cada ejecución del bloque agrega el número dado al total actual y luego almacena el resultado nuevamente en el acumulador. La siguiente llamada de bloque tiene este nuevo valor, se agrega, lo almacena nuevamente y se repite.
Al final del proceso, inject devuelve el acumulador, que en este caso es la suma de todos los valores de la matriz, o 10.
Aquí hay otro ejemplo simple para crear un hash a partir de una matriz de objetos, clave por su representación de cadena:
En este caso, estamos predeterminando nuestro acumulador a un hash vacío, y luego lo llenamos cada vez que se ejecuta el bloque. 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.
fuente
result + explanation
es tanto la transformación al acumulador como el valor de retorno. Es la última línea del bloque, por lo que es un retorno implícito.inject
toma un valor para comenzar (el0
en su ejemplo) y un bloque, y ejecuta ese bloque una vez para cada elemento de la lista.result + element
).La forma más fácil de explicar esto puede ser mostrar cómo funciona cada paso, por ejemplo; Este es un conjunto imaginario de pasos que muestran cómo se puede evaluar este resultado:
fuente
La sintaxis para el método de inyección es la 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 el 10 como salida.
Entonces, antes de comenzar, veamos cuáles son los valores almacenados en cada variable:
resultado = 0 El cero vino de inyectar (valor) que es 0
element = 1 Es el primer elemento de la matriz.
¡¡¡Bueno!!! Entonces, comencemos 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í los valores en negrita y cursiva son elementos que se obtienen de la matriz y los valores en negrita son los valores resultantes.
Espero que entiendas el funcionamiento del
#inject
método de#ruby
.fuente
El código itera sobre los cuatro elementos dentro de la matriz y agrega el resultado anterior al elemento actual:
fuente
Lo que dijeron, pero tenga en cuenta también que no siempre necesita proporcionar un "valor inicial":
es lo mismo que
Pruébalo, te espero.
Cuando no se pasa ningún argumento para inyectar, los dos primeros 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.
fuente
El número que coloca dentro de su () de inyección representa un lugar inicial, podría ser 0 o 1000. Dentro de las tuberías tiene dos marcadores de posición | x, y |. x = cualquier número que tengas dentro del .inject ('x'), y el segundo representa cada iteración de tu objeto.
[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15
fuente
Inject aplica el bloque
a cada elemento de la matriz. Para el siguiente elemento ("elemento"), el valor devuelto por el bloque es "resultado". Como lo ha llamado (con un parámetro), "resultado" comienza con el valor de ese parámetro. Entonces el efecto es sumar los elementos.
fuente
tldr;
inject
difiere demap
una manera importante:inject
devuelve el valor de la última ejecución del bloque, mientras quemap
devuelve la matriz sobre la que iteró.Más que eso, el valor de cada ejecución de bloque pasó a la siguiente ejecución a través del primer parámetro (
result
en este caso) y puede inicializar ese valor (la(0)
parte).Su ejemplo anterior podría escribirse
map
así:Mismo efecto pero
inject
es más conciso aquí.A menudo encontrará que una tarea ocurre en el
map
bloque, mientras que una evaluación ocurre en elinject
bloque.El método que elija depende del alcance que desee
result
. Cuándo no usarlo sería algo como esto:Puede decir: "Mírame, acabo de combinar todo en una sola línea", pero también asignaste temporalmente la memoria
x
como una variable temporal que no era necesaria ya que ya teníasresult
que trabajar.fuente
es equivalente a lo siguiente:
fuente
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
En inglés simple, está pasando (iterando) a través de esta matriz (
[1,2,3,4]
). Recorrerá 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úa: 3 + 3 es igual a 6. Y finalmente, 6 + 4 es igual a 10.fuente
Este código no permite la posibilidad de no pasar un valor inicial, pero puede ayudar a explicar lo que está sucediendo.
fuente
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 te confunde o por qué tienes un valor en el método? Buena pregunta sin embargo. ¿Cuál es el método del operador allí?
¿Cómo comienza?
¿Podemos hacer esto?
¿Esto funciona?
Verá, estoy construyendo sobre la idea de que simplemente suma todos los elementos de la matriz y produce un número en la nota que ve en los documentos.
Siempre puedes hacer esto
para ver cómo se puede iterar el enumerable de la matriz. 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 intentar obtener un resultado
pero nada regresa, así que esto actúa igual que antes
en el bloque inspector de elementos.
fuente
Esta es una explicación simple y bastante fácil de entender:
Olvídate del "valor inicial", ya que es algo confuso al principio.
Puede entender lo anterior como: Estoy inyectando una "máquina sumadora" entre 1,2,3,4. Es decir, es 1 ♫ 2 ♫ 3 ♫ 4 y ♫ es una máquina sumadora, por lo que es lo mismo que 1 + 2 + 3 + 4, y es 10.
De hecho, puede inyectar un
+
entre ellos:y es como, inyectar un
+
entre 1,2,3,4, haciéndolo 1 + 2 + 3 + 4 y es 10. Esta:+
es la forma de Ruby de especificar+
en forma de un símbolo.Esto es bastante fácil de entender e intuitivo. Y si desea analizar cómo funciona paso a paso, es como: tomar 1 y 2, y ahora agregarlos, y cuando tenga un resultado, guárdelo primero (que es 3), y ahora, el siguiente es el almacenado valor 3 y el elemento de matriz 3 que pasa por el proceso a + b, que es 6, y ahora almacena este valor, y ahora 6 y 4 pasan por el proceso a + b, y es 10. Usted esencialmente está haciendo
y es 10. El "valor inicial"
0
es solo una "base" para empezar. En muchos casos, no lo necesitas. Imagínese si necesita 1 * 2 * 3 * 4 y esy ya está hecho. No necesita un "valor inicial" de
1
para multiplicar todo1
.fuente
Hay otra forma de método .inject () que es muy útil [4,5] .inject (&: +) que sumará todos los elementos del área
fuente
Es justo
reduce
ofold
, si estás familiarizado con otros idiomas.fuente
Es lo mismo que esto:
fuente