Besoin d'une explication simple de la méthode d'injection
142
[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?
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 passé à la prochaine exécution du bloc. Dans le cas du code ci-dessus, vous réglez 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, y ajoute, le stocke à nouveau et se répète.
À la fin du processus, inject renvoie 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é 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 définissons par défaut notre accumulateur sur un hachage vide, puis nous le remplissons à chaque fois que le bloc s'exécute. Remarquez que nous devons retourner le hachage comme dernière ligne du bloc, car le résultat du bloc sera stocké dans l'accumulateur.
grande explication, cependant, dans l'exemple donné par l'OP, ce qui est retourné (comme le hachage est dans votre exemple). Il se termine par résultat + explication et devrait avoir une valeur de retour, oui?
Projjol le
1
@Projjol the result + explanationest à la fois la transformation en accumulateur et la valeur de retour. C'est la dernière ligne du bloc, ce qui en fait un retour implicite.
KA01 du
87
injectprend une valeur pour commencer (le 0dans votre exemple), et un bloc, et il exécute ce bloc une fois pour chaque élément de la liste.
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).
Il exécute ensuite à nouveau le bloc, en transmettant le résultat de la première itération comme premier argument et le deuxième élément de la liste comme deuxième argument, enregistrant à nouveau le résultat.
Il continue ainsi jusqu'à ce qu'il ait consommé tous les éléments de la liste.
La manière la plus simple d'expliquer cela peut être de montrer comment chaque étape fonctionne, par 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)+410
Merci d'avoir écrit les étapes. Cela a beaucoup aidé. Bien que je sois un peu confus quant à savoir si vous voulez dire que le diagramme ci-dessous montre comment la méthode inject est implémentée en dessous en termes de ce qui est passé en tant qu'arguments à injecter.
2
Le diagramme ci-dessous est basé sur la façon dont il pourrait être mis en œuvre; il n'est pas nécessairement mis en œuvre exactement de cette façon. C'est pourquoi j'ai dit que c'était un ensemble imaginaire d'étapes; il montre la structure de base, mais pas la mise en œuvre exacte.
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, j'attendrai.
Lorsqu'aucun argument n'est passé à injecter, les deux premiers éléments 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.
Le nombre que vous mettez dans votre () d'inject représente un point de départ, il pourrait être 0 ou 1000. À l'intérieur des tuyaux, vous avez deux espaces réservés | x, y |. x = quel que soit le nombre que vous aviez dans le .inject ('x'), et la seconde représente chaque itération de votre objet.
[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15
à chaque élément du tableau. Pour l'élément suivant ("élément"), la valeur renvoyée par le bloc est "résultat". La façon dont vous l'avez appelé (avec un paramètre), "result" commence par la valeur de ce paramètre. Donc, l'effet est d'ajouter les éléments.
tldr; injectdiffère d' mapune manière importante: injectrenvoie la valeur de la dernière exécution du bloc tandis que mapretourne le tableau sur lequel il a itéré.
Plus que cela, la valeur de chaque exécution de bloc est passée à l'exécution suivante via le premier paramètre ( resultdans ce cas) et vous pouvez initialiser cette valeur (la (0)partie).
Votre exemple ci-dessus pourrait être écrit en utilisant mapcomme ceci:
result =0# initialize result[1,2,3,4].map {|element| result += element }# result => 10
Même effet mais injectest plus concis ici.
Vous constaterez souvent qu'une affectation se produit dans le mapbloc, tandis qu'une évaluation se produit dans le injectbloc.
La méthode que vous choisissez dépend de la portée que vous souhaitez result. Quand ne pas l' utiliser, ce serait quelque chose comme ceci:
result =[1,2,3,4].inject(0){|x, element| x + element }
Vous pourriez être comme tout le monde, "Regardez-moi, je viens de combiner tout cela en une seule ligne", mais vous avez également temporairement alloué de la mémoire pour xune variable de scratch qui n'était pas nécessaire puisque vous deviez déjà resulttravailler avec.
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
En clair, vous parcourez (itérer) 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). Cela sera maintenant enregistré comme résultat. Continuez: 3 + 3 égale 6. Et enfin, 6 + 4 égale 10.
Ce code ne permet pas de ne pas transmettre de 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
Est-ce le bloc qui vous déroute ou pourquoi vous avez une valeur dans la méthode? Bonne question cependant. Quelle est la méthode opérateur là-bas?
result.+
Comment cela commence-t-il?
#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 que je m’appuie sur l’idée que cela résume simplement tous les éléments du tableau et donne un nombre dans le mémo que vous voyez dans la documentation.
Tu peux toujours faire ça
[1,2,3,4].each {|element| p element }
pour voir l'énumérable du tableau être itéré. C'est l'idée de base.
C'est juste qu'injecter ou réduire 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 ça agit de la même manière qu'avant
[1,2,3,4].each {|result =0, element| p result + element }
C'est une explication simple et assez facile à comprendre:
Oubliez la «valeur initiale» car elle est quelque 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, donc c'est la même chose que 1 + 2 + 3 + 4, et c'est 10.
Vous pouvez en fait injecter un +entre eux:
>[1,2,3,4].inject(:+)=>10
et c'est comme, injecter un +entre 1, 2, 3, 4, ce qui en fait 1 + 2 + 3 + 4 et c'est 10. C'est :+la manière de Ruby de spécifier +sous la forme d'un symbole.
C'est assez facile à comprendre et intuitif. Et si vous voulez analyser son fonctionnement é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, le suivant est le stocké 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 vaut 10. La "valeur initiale" 0est juste 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 1pour multiplier le tout avec 1.
Réponses:
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 passé à la prochaine exécution du bloc. Dans le cas du code ci-dessus, vous réglez 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, y ajoute, le stocke à nouveau et se répète.
À la fin du processus, inject renvoie 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é par leur représentation sous forme de chaîne:
Dans ce cas, nous définissons par défaut notre accumulateur sur un hachage vide, puis nous le remplissons à chaque fois que le bloc s'exécute. Remarquez que nous devons retourner le hachage comme dernière ligne du bloc, car le résultat du bloc sera stocké dans l'accumulateur.
la source
result + explanation
est à la fois la transformation en accumulateur et la valeur de retour. C'est la dernière ligne du bloc, ce qui en fait un retour implicite.inject
prend une valeur pour commencer (le0
dans votre exemple), et un bloc, et il exécute ce bloc une fois pour chaque élément de la liste.result + element
).La manière la plus simple d'expliquer cela peut être de montrer comment chaque étape fonctionne, par exemple; il s'agit d'un ensemble imaginaire d'étapes montrant comment ce résultat pourrait être évalué:
la source
La syntaxe de la méthode inject est la suivante:
inject (value_initial) { |result_memo, object| block }
Résolvons l'exemple ci-dessus ie
[1, 2, 3, 4].inject(0) { |result, element| result + element }
ce qui donne le 10 comme sortie.
Donc, avant de commencer, voyons quelles sont les valeurs stockées dans chaque variable:
result = 0 Le zéro provient de inject (valeur) qui est 0
element = 1 C'est le premier élément du tableau.
Bien!!! Alors, commençons à 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, les valeurs Bold-Italic sont des éléments extraits du tableau et les valeurs simplement Bold sont les valeurs résultantes.
J'espère que vous comprenez le fonctionnement de la
#inject
méthode du#ruby
.la source
Le code itère sur les quatre éléments du tableau et ajoute le résultat précédent à l'élément actuel:
la source
Ce qu'ils ont dit, mais notez également que vous n'avez pas toujours besoin de fournir une "valeur de départ":
est le même que
Essayez-le, j'attendrai.
Lorsqu'aucun argument n'est passé à injecter, les deux premiers éléments 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.
la source
Le nombre que vous mettez dans votre () d'inject représente un point de départ, il pourrait être 0 ou 1000. À l'intérieur des tuyaux, vous avez deux espaces réservés | x, y |. x = quel que soit le nombre que vous aviez dans le .inject ('x'), et la seconde représente chaque itération de votre objet.
[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15
la source
Inject applique le bloc
à chaque élément du tableau. Pour l'élément suivant ("élément"), la valeur renvoyée par le bloc est "résultat". La façon dont vous l'avez appelé (avec un paramètre), "result" commence par la valeur de ce paramètre. Donc, l'effet est d'ajouter les éléments.
la source
tldr;
inject
diffère d'map
une manière importante:inject
renvoie la valeur de la dernière exécution du bloc tandis quemap
retourne le tableau sur lequel il a itéré.Plus que cela, la valeur de chaque exécution de bloc est passée à l'exécution suivante via le premier paramètre (
result
dans ce cas) et vous pouvez initialiser cette valeur (la(0)
partie).Votre exemple ci-dessus pourrait être écrit en utilisant
map
comme ceci:Même effet mais
inject
est plus concis ici.Vous constaterez souvent qu'une affectation se produit dans le
map
bloc, tandis qu'une évaluation se produit dans leinject
bloc.La méthode que vous choisissez dépend de la portée que vous souhaitez
result
. Quand ne pas l' utiliser, ce serait quelque chose comme ceci:Vous pourriez être comme tout le monde, "Regardez-moi, je viens de combiner tout cela en une seule ligne", mais vous avez également temporairement alloué de la mémoire pour
x
une variable de scratch qui n'était pas nécessaire puisque vous deviez déjàresult
travailler avec.la source
équivaut à ce qui suit:
la source
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
En clair, vous parcourez (itérer) 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). Cela sera maintenant enregistré comme résultat. Continuez: 3 + 3 égale 6. Et enfin, 6 + 4 égale 10.la source
Ce code ne permet pas de ne pas transmettre de valeur de départ, mais peut aider à expliquer ce qui se passe.
la source
Commencez ici, puis 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 déroute ou pourquoi vous avez une valeur dans la méthode? Bonne question cependant. Quelle est la méthode opérateur là-bas?
Comment cela commence-t-il?
Pouvons-nous faire cela?
Est-ce que ça marche?
Vous voyez que je m’appuie sur l’idée que cela résume simplement tous les éléments du tableau et donne un nombre dans le mémo que vous voyez dans la documentation.
Tu peux toujours faire ça
pour voir l'énumérable du tableau être itéré. C'est l'idée de base.
C'est juste qu'injecter ou réduire vous donne un mémo ou un accumulateur qui est envoyé.
On pourrait essayer d'obtenir un résultat
mais rien ne revient donc ça agit de la même manière qu'avant
dans le bloc inspecteur d'élément.
la source
C'est une explication simple et assez facile à comprendre:
Oubliez la «valeur initiale» car elle est quelque peu déroutante au début.
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, donc c'est la même chose que 1 + 2 + 3 + 4, et c'est 10.
Vous pouvez en fait injecter un
+
entre eux:et c'est comme, injecter un
+
entre 1, 2, 3, 4, ce qui en fait 1 + 2 + 3 + 4 et c'est 10. C'est:+
la manière de Ruby de spécifier+
sous la forme d'un symbole.C'est assez facile à comprendre et intuitif. Et si vous voulez analyser son fonctionnement é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, le suivant est le stocké 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
et vaut 10. La "valeur initiale"
0
est juste 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'estet c'est fait. Vous n'avez pas besoin d'une "valeur initiale" de
1
pour multiplier le tout avec1
.la source
Il existe une autre forme de méthode .inject () qui est très utile [4,5] .inject (&: +) Cela ajoutera tous les éléments de la zone
la source
C'est juste
reduce
oufold
, si vous connaissez d'autres langues.la source
Est-ce le même que ceci:
la source