web-dev-qa-db-fra.com

Besoin d'une explication simple de la méthode d'injection

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

Je regarde ce code mais mon cerveau n'enregistre pas comment le nombre 10 peut devenir le résultat. Quelqu'un voudrait-il expliquer ce qui se passe ici?

133
hightide

Vous pouvez considérer le premier argument de bloc comme un accumulateur: le résultat de chaque exécution du bloc est stocké dans l'accumulateur, puis transmis à la prochaine exécution du bloc. Dans le cas du code illustré ci-dessus, vous placez par défaut l'accumulateur, résultat, sur 0. Chaque exécution du bloc ajoute le nombre donné au total actuel, puis stocke le résultat dans l'accumulateur. L'appel de bloc suivant a cette nouvelle valeur, l'ajoute, la stocke à nouveau et se répète.

À la fin du processus, inject retourne l'accumulateur, qui dans ce cas est la somme de toutes les valeurs du tableau, ou 10.

Voici un autre exemple simple pour créer un hachage à partir d'un tableau d'objets, indexés par leur représentation sous forme de chaîne:

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

Dans ce cas, nous mettons par défaut notre accumulateur sur un hachage vide, puis le remplissons à chaque exécution du bloc. Notez que nous devons renvoyer le hachage comme dernière ligne du bloc, car le résultat du bloc sera stocké dans l'accumulateur.

197
Drew Olson

inject prend une valeur pour commencer (le 0 dans votre exemple), et un bloc, et il exécute ce bloc une fois pour chaque élément de la liste.

  1. Lors de la première itération, il transmet la valeur que vous avez fournie comme valeur de départ et le premier élément de la liste, et il enregistre la valeur renvoyée par votre bloc (dans ce cas result + element).
  2. Il exécute ensuite à nouveau le bloc, en transmettant le résultat de la première itération en tant que premier argument et le deuxième élément de la liste en tant que deuxième argument, puis en enregistrant à nouveau le résultat.
  3. Il continue ainsi jusqu'à ce qu'il ait consommé tous les éléments de la liste.

La façon la plus simple d'expliquer cela peut être de montrer comment chaque étape fonctionne, pour votre exemple; il s'agit d'un ensemble imaginaire d'étapes montrant comment ce résultat pourrait être évalué:

[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
81
Brian Campbell

La syntaxe de la méthode d'injection est la suivante:

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

Résolvons l'exemple ci-dessus, c'est-à-dire.

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

ce qui donne 1 comme sortie.

Donc, avant de commencer, voyons quelles sont les valeurs stockées dans chaque variable:

résultat = Le zéro vient de inject (valeur) qui est

element = 1 C'est le premier élément du tableau.

Bien!!! Commençons donc à comprendre l'exemple ci-dessus

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

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

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

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

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

Ici Gras italique les valeurs sont des éléments extraits du tableau et simplement Audacieux les valeurs sont les valeurs résultantes.

J'espère que vous comprenez le fonctionnement du #inject méthode du #Ruby.

26
Vishal Nagda

Le code parcourt les quatre éléments du tableau et ajoute le résultat précédent à l'élément actuel:

  • 1 + 2 = 3
  • 3 + 3 = 6
  • 6 + 4 = 10
18
John Topley

Le nombre que vous mettez à l'intérieur de votre () injecte représente un point de départ, il pourrait être 0 ou 1000. À l'intérieur des tuyaux, vous avez deux emplacements | x, y |. x = quel nombre que vous ayez eu à l'intérieur du .inject ('x'), et le deuxième représente chaque itération de votre objet.

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

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

14
Stuart G

Ce qu'ils ont dit, mais notez également que vous n'avez pas toujours besoin de fournir une "valeur de départ":

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

est le même que

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

Essayez-le, je vais attendre.

Quand aucun argument n'est passé pour injecter, les premiers éléments two sont passés dans la première itération. Dans l'exemple ci-dessus, le résultat est 1 et l'élément est 2 la première fois, donc un appel de moins est effectué vers le bloc.

14
Mike Woodhouse

Injecter applique le bloc

result + element

à chaque élément du tableau. Pour l'élément suivant ("élément"), la valeur renvoyée par le bloc est "résultat". Comme vous l'avez appelé (avec un paramètre), "résultat" commence par la valeur de ce paramètre. L'effet consiste donc à additionner les éléments.

6
Sam Hoice

tldr;inject diffère de map d'une manière importante: inject renvoie la valeur de la dernière exécution du bloc tandis que map renvoie le tableau sur lequel il a itéré.

Plus que cela la valeur de chaque exécution de bloc passée à la prochaine exécution via le premier paramètre (result dans ce cas) et vous pouvez initialiser cette valeur (le (0) partie).

Votre exemple ci-dessus pourrait être écrit en utilisant map comme ceci:

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

Même effet mais inject est plus concis ici.

Vous constaterez souvent qu'une affectation se produit dans le bloc map, tandis qu'une évaluation se produit dans le bloc inject.

La méthode que vous choisissez dépend de la portée que vous souhaitez pour result. Quand ne pas utiliser, ce serait quelque chose comme ceci:

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

Vous pourriez être comme tous, "Regardez-moi, je viens de combiner tout cela en une seule ligne", mais vous avez également alloué temporairement de la mémoire pour x en tant que variable de travail qui n'était pas nécessaire car vous aviez déjà result travailler avec.

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

est équivalent à ce qui suit:

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
4
Fred Willmore

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

En clair, vous parcourez (itérez) ce tableau ([1,2,3,4]). Vous parcourrez ce tableau 4 fois, car il y a 4 éléments (1, 2, 3 et 4). La méthode inject a 1 argument (le nombre 0) et vous ajouterez cet argument au 1er élément (0 + 1. Cela équivaut à 1). 1 est enregistré dans le "résultat". Ensuite, vous ajoutez ce résultat (qui est 1) à l'élément suivant (1 + 2. C'est 3). This sera maintenant enregistré comme résultat. Continuez: 3 + 3 est égal à 6. Et enfin, 6 + 4 est égal à 10.

3
Maddie

Ce code ne permet pas de ne pas passer une valeur de départ, mais peut aider à expliquer ce qui se passe.

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
2
Andrew Grimm

Commencez ici et passez en revue toutes les méthodes qui prennent des blocs. http://Ruby-doc.org/core-2.3.3/Enumerable.html#method-i-inject

Est-ce le bloc qui vous embrouille ou pourquoi vous avez une valeur dans la méthode? Bonne question cependant. Quelle est la méthode opérateur là-bas?

result.+

Comment ça commence?

#inject(0)

Pouvons-nous faire cela?

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

Est-ce que ça marche?

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

Vous voyez, je m'appuie sur l'idée qu'il résume simplement tous les éléments du tableau et donne un nombre dans le mémo que vous voyez dans les documents.

Vous pouvez toujours faire ça

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

pour voir l'énumérable du tableau être itéré. Voilà l'idée de base.

C'est juste que l'injection ou la réduction vous donne un mémo ou un accumulateur qui est envoyé.

On pourrait essayer d'obtenir un résultat

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

mais rien ne revient donc cela agit comme avant

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

dans le bloc d'inspection des éléments.

1
Douglas G. Allen

Il existe une autre forme de méthode .inject () qui est très utile [4,5] .inject (&: +) qui additionnera tous les éléments de la zone

0

Ceci est une explication simple et assez facile à comprendre:

Oubliez la "valeur initiale" car elle est un peu déroutante au début.

> [1,2,3,4].inject{|a,b| a+b}
=> 10

Vous pouvez comprendre ce qui précède comme suit: J'injecte une "machine à ajouter" entre 1,2,3,4. Cela signifie que c'est 1 ♫ 2 ♫ 3 ♫ 4 et ♫ est une machine à additionner, c'est donc la même chose que 1 + 2 + 3 + 4, et c'est 10.

Vous pouvez réellement injecter un + entre eux:

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

et c'est comme, injectez un + entre 1,2,3,4, ce qui en fait 1 + 2 + 3 + 4 et il est 10. Le :+ est la façon dont Ruby spécifie + sous la forme d'un symbole.

C'est assez facile à comprendre et intuitif. Et si vous voulez analyser comment cela fonctionne étape par étape, c'est comme: prendre 1 et 2, et maintenant les ajouter, et quand vous avez un résultat, stockez-le d'abord (qui est 3), et maintenant, ensuite est stocké la valeur 3 et l'élément de tableau 3 passant par le processus a + b, qui est 6, et stockent maintenant cette valeur, et maintenant 6 et 4 passent par le processus a + b, et est 10. Vous faites essentiellement

((1 + 2) + 3) + 4

et est 10. La "valeur initiale" 0 n'est qu'une "base" pour commencer. Dans de nombreux cas, vous n'en avez pas besoin. Imaginez si vous avez besoin de 1 * 2 * 3 * 4 et c'est

[1,2,3,4].inject(:*)
=> 24

et c'est fait. Vous n'avez pas besoin d'une "valeur initiale" de 1 pour multiplier le tout par 1.

0

C'est juste reduce ou fold, si vous connaissez d'autres langues.

0
Nick