[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í?
[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í?
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:
[1,"a",Object.new,:hi].inject({}) do |hash, item|
hash[item.to_s] = item
hash
end
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.
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 (el 0
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:
[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
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
.
El código itera sobre los cuatro elementos dentro de la matriz y agrega el resultado anterior al elemento actual:
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
1 + 5 = 6 2 + 6 = 8 3 + 8 = 11 11 + 4 = 15
Inject aplica el bloque
result + element
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; inject
difiere de map
una manera importante: inject
devuelve el valor de la última ejecución del bloque, mientras que map
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í:
result = 0 # initialize result
[1, 2, 3, 4].map { |element| result += element }
# result => 10
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 el inject
bloque.
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 x
como una variable temporal que no era necesaria ya que ya tenías result
que trabajar.
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
es equivalente a lo 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
[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
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í?
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 }
en el bloque inspector de elementos.
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" 0
es 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 1
para multiplicar todo 1
.
Hay otra forma de método .inject () que es muy útil [4,5] .inject (&: +) que sumará todos los elementos del área
Es lo mismo que esto:
[1,2,3,4].inject(:+)
=> 10