Benötigen Sie eine einfache Erklärung der Injektionsmethode
142
[1,2,3,4].inject(0){|result, element| result + element }# => 10
Ich schaue auf diesen Code, aber mein Gehirn registriert nicht, wie die Zahl 10 zum Ergebnis werden kann. Würde es jemandem etwas ausmachen zu erklären, was hier passiert?
Sie können sich das erste Blockargument als Akkumulator vorstellen: Das Ergebnis jedes Blocklaufs wird im Akkumulator gespeichert und dann an die nächste Ausführung des Blocks übergeben. Im Fall des oben gezeigten Codes setzen Sie das Ergebnis des Akkumulators standardmäßig auf 0. Jeder Lauf des Blocks addiert die angegebene Zahl zur aktuellen Summe und speichert das Ergebnis dann wieder im Akkumulator. Der nächste Blockaufruf hat diesen neuen Wert, fügt ihn hinzu, speichert ihn erneut und wiederholt ihn.
Am Ende des Prozesses gibt inj den Akkumulator zurück, in diesem Fall die Summe aller Werte im Array oder 10.
Hier ist ein weiteres einfaches Beispiel zum Erstellen eines Hash aus einem Array von Objekten, die durch ihre Zeichenfolgendarstellung gekennzeichnet sind:
[1,"a",Object.new,:hi].inject({})do|hash, item|
hash[item.to_s]= item
hash
end
In diesem Fall setzen wir unseren Akkumulator standardmäßig auf einen leeren Hash und füllen ihn dann jedes Mal auf, wenn der Block ausgeführt wird. Beachten Sie, dass wir den Hash als letzte Zeile des Blocks zurückgeben müssen, da das Ergebnis des Blocks wieder im Akkumulator gespeichert wird.
Eine gute Erklärung ist jedoch, dass in dem vom OP gegebenen Beispiel zurückgegeben wird, was zurückgegeben wird (wie Hash in Ihrem Beispiel). Es endet mit Ergebnis + Erklärung und sollte einen Rückgabewert haben, ja?
Projjol
1
@Projjol das result + explanationist sowohl die Transformation zum Akkumulator als auch der Rückgabewert. Es ist die letzte Zeile im Block, die eine implizite Rückgabe darstellt.
KA01
87
injectNimmt zunächst einen Wert (den 0in Ihrem Beispiel) und einen Block und führt diesen Block einmal für jedes Element der Liste aus.
Bei der ersten Iteration wird der von Ihnen als Startwert angegebene Wert und das erste Element der Liste übergeben und der von Ihrem Block zurückgegebene Wert (in diesem Fall result + element) gespeichert .
Anschließend wird der Block erneut ausgeführt, wobei das Ergebnis der ersten Iteration als erstes Argument und das zweite Element aus der Liste als zweites Argument übergeben werden, wobei das Ergebnis erneut gespeichert wird.
Dies wird so fortgesetzt, bis alle Elemente der Liste verbraucht sind.
Der einfachste Weg, dies zu erklären, besteht darin, für Ihr Beispiel zu zeigen, wie jeder Schritt funktioniert. Dies ist eine imaginäre Reihe von Schritten, die zeigen, wie dieses Ergebnis bewertet werden kann:
[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
Vielen Dank, dass Sie die Schritte ausgeschrieben haben. Das hat sehr geholfen. Obwohl ich ein wenig verwirrt war, ob Sie damit meinen, dass das folgende Diagramm zeigt, wie die Inject-Methode darunter implementiert wird, was als Argumente für das Injizieren übergeben wird.
2
Das folgende Diagramm basiert auf , wie es könnte umgesetzt werden; es ist nicht unbedingt genau so implementiert. Deshalb habe ich gesagt, es ist eine imaginäre Reihe von Schritten; Es zeigt die Grundstruktur, aber nicht die genaue Implementierung.
Brian Campbell
27
Die Syntax für die Inject-Methode lautet wie folgt:
Was sie gesagt haben, aber beachten Sie auch, dass Sie nicht immer einen "Startwert" angeben müssen:
[1,2,3,4].inject(0){|result, element| result + element }# => 10
ist das gleiche wie
[1,2,3,4].inject {|result, element| result + element }# => 10
Probieren Sie es aus, ich werde warten.
Wenn kein Argument zum Injizieren übergeben wird, werden die ersten beiden Elemente an die erste Iteration übergeben. Im obigen Beispiel ist das Ergebnis 1 und das Element beim ersten Mal 2, sodass der Block weniger aufgerufen wird.
Die Zahl, die Sie in Ihr () der Injektion eingeben, stellt einen Startplatz dar. Sie kann 0 oder 1000 sein. In den Rohren befinden sich zwei Platzhalter | x, y |. x = welche Zahl auch immer Sie in der .inject ('x') hatten, und die Sekunde repräsentiert jede Iteration Ihres Objekts.
[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15
zu jedem Element im Array. Für das nächste Element ("Element") lautet der vom Block zurückgegebene Wert "Ergebnis". So wie Sie es genannt haben (mit einem Parameter), beginnt "Ergebnis" mit dem Wert dieses Parameters. Der Effekt addiert also die Elemente.
tldr; injectunterscheidet sich mapin einem wichtigen Punkt: injectGibt den Wert der letzten Ausführung des Blocks zurück, während mapdas Array zurückgegeben wird, über das iteriert wurde.
Darüber hinaus wurde der Wert jeder Blockausführung über den ersten Parameter ( resultin diesem Fall) an die nächste Ausführung übergeben, und Sie können diesen Wert (das (0)Teil) initialisieren .
Ihr obiges Beispiel könnte folgendermaßen geschrieben werden map:
result =0# initialize result[1,2,3,4].map {|element| result += element }# result => 10
Gleicher Effekt, aber injecthier prägnanter.
Sie werden häufig feststellen, dass eine Zuweisung im mapBlock erfolgt, während eine Auswertung im injectBlock erfolgt.
Welche Methode Sie wählen, hängt vom gewünschten Umfang ab result. Wann man es nicht benutzt, wäre ungefähr so:
result =[1,2,3,4].inject(0){|x, element| x + element }
Sie mögen wie alle sein: "Sieh mal, ich habe das alles nur in einer Zeile zusammengefasst", aber Sie haben auch vorübergehend Speicher xals resultArbeitsvariable zugewiesen , die nicht erforderlich war, da Sie bereits damit arbeiten mussten.
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Im Klartext durchlaufen Sie dieses Array ( [1,2,3,4]). Sie werden dieses Array viermal durchlaufen, da es vier Elemente gibt (1, 2, 3 und 4). Die Inject-Methode hat 1 Argument (die Nummer 0), und Sie fügen dieses Argument dem 1. Element hinzu (0 + 1. Dies entspricht 1). 1 wird im "Ergebnis" gespeichert. Dann addieren Sie dieses Ergebnis (das 1 ist) zum nächsten Element (1 + 2. Dies ist 3). Dies wird nun als Ergebnis gespeichert. Mach weiter: 3 + 3 ist gleich 6. Und schließlich ist 6 + 4 gleich 10.
Dieser Code erlaubt nicht die Möglichkeit, keinen Startwert zu übergeben, kann aber helfen, zu erklären, was los ist.
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
Ist es der Block, der Sie verwirrt oder warum Sie einen Wert in der Methode haben? Gute Frage. Was ist die Operatormethode dort?
result.+
Wie fängt es an?
#inject(0)
Können wir das machen?
[1,2,3,4].inject(0){|result, element| result.+ element }
Funktioniert das?
[1,2,3,4].inject(){|result =0, element| result.+ element }
Sie sehen, ich baue auf der Idee auf, dass es einfach alle Elemente des Arrays summiert und eine Zahl in dem Memo ergibt, das Sie in den Dokumenten sehen.
Sie können dies immer tun
[1,2,3,4].each {|element| p element }
um zu sehen, wie die Aufzählung des Arrays durchlaufen wird. Das ist die Grundidee.
Es ist nur so, dass Sie durch Injizieren oder Reduzieren ein Memo oder einen Akku erhalten, der verschickt wird.
Wir könnten versuchen, ein Ergebnis zu erzielen
[1,2,3,4].each {|result =0, element| result + element }
aber nichts kommt zurück, so dass dies genauso verhält wie zuvor
[1,2,3,4].each {|result =0, element| p result + element }
Dies ist eine einfache und ziemlich leicht verständliche Erklärung:
Vergessen Sie den "Anfangswert", da er am Anfang etwas verwirrend ist.
>[1,2,3,4].inject{|a,b| a+b}=>10
Sie können das Obige wie folgt verstehen: Ich injiziere eine "Addiermaschine" zwischen 1,2,3,4. Das heißt, es ist 1 ♫ 2 ♫ 3 ♫ 4 und ♫ ist eine Addiermaschine, also ist es dasselbe wie 1 + 2 + 3 + 4 und es ist 10.
Sie können tatsächlich ein +dazwischen spritzen :
>[1,2,3,4].inject(:+)=>10
und es ist so, als würde man ein +zwischen 1,2,3,4 injizieren , was es zu 1 + 2 + 3 + 4 macht und es ist 10. Das :+ist Rubys Art, +in Form eines Symbols zu spezifizieren .
Dies ist recht einfach zu verstehen und intuitiv. Und wenn Sie Schritt für Schritt analysieren möchten, wie es funktioniert, ist es wie folgt: Nehmen Sie 1 und 2 und fügen Sie sie jetzt hinzu. Wenn Sie ein Ergebnis haben, speichern Sie es zuerst (das ist 3), und jetzt wird das nächste gespeichert Wert 3 und das Array-Element 3 durchlaufen den a + b-Prozess, der 6 ist, und speichern jetzt diesen Wert, und jetzt durchlaufen 6 und 4 den a + b-Prozess und sind 10. Sie tun dies im Wesentlichen
((1+2)+3)+4
und ist 10. Der "Anfangswert" 0ist zunächst nur eine "Basis". In vielen Fällen brauchen Sie es nicht. Stellen Sie sich vor, Sie brauchen 1 * 2 * 3 * 4 und es ist
[1,2,3,4].inject(:*)=>24
und es ist geschafft. Sie brauchen keinen "Anfangswert" von 1, um das Ganze mit zu multiplizieren 1.
Antworten:
Sie können sich das erste Blockargument als Akkumulator vorstellen: Das Ergebnis jedes Blocklaufs wird im Akkumulator gespeichert und dann an die nächste Ausführung des Blocks übergeben. Im Fall des oben gezeigten Codes setzen Sie das Ergebnis des Akkumulators standardmäßig auf 0. Jeder Lauf des Blocks addiert die angegebene Zahl zur aktuellen Summe und speichert das Ergebnis dann wieder im Akkumulator. Der nächste Blockaufruf hat diesen neuen Wert, fügt ihn hinzu, speichert ihn erneut und wiederholt ihn.
Am Ende des Prozesses gibt inj den Akkumulator zurück, in diesem Fall die Summe aller Werte im Array oder 10.
Hier ist ein weiteres einfaches Beispiel zum Erstellen eines Hash aus einem Array von Objekten, die durch ihre Zeichenfolgendarstellung gekennzeichnet sind:
In diesem Fall setzen wir unseren Akkumulator standardmäßig auf einen leeren Hash und füllen ihn dann jedes Mal auf, wenn der Block ausgeführt wird. Beachten Sie, dass wir den Hash als letzte Zeile des Blocks zurückgeben müssen, da das Ergebnis des Blocks wieder im Akkumulator gespeichert wird.
quelle
result + explanation
ist sowohl die Transformation zum Akkumulator als auch der Rückgabewert. Es ist die letzte Zeile im Block, die eine implizite Rückgabe darstellt.inject
Nimmt zunächst einen Wert (den0
in Ihrem Beispiel) und einen Block und führt diesen Block einmal für jedes Element der Liste aus.result + element
) gespeichert .Der einfachste Weg, dies zu erklären, besteht darin, für Ihr Beispiel zu zeigen, wie jeder Schritt funktioniert. Dies ist eine imaginäre Reihe von Schritten, die zeigen, wie dieses Ergebnis bewertet werden kann:
quelle
Die Syntax für die Inject-Methode lautet wie folgt:
inject (value_initial) { |result_memo, object| block }
Lassen Sie uns das obige Beispiel lösen, dh
[1, 2, 3, 4].inject(0) { |result, element| result + element }
das gibt die 10 als Ausgabe.
Bevor wir beginnen, wollen wir uns ansehen, welche Werte in den einzelnen Variablen gespeichert sind:
Ergebnis = 0 Die Null kam von injizieren (Wert), was 0 ist
element = 1 Es ist das erste Element des Arrays.
Okey !!! Beginnen wir also mit dem Verständnis des obigen Beispiels
Schritt 1
[1, 2, 3, 4].inject(0) { |0, 1| 0 + 1 }
Schritt 2
[1, 2, 3, 4].inject(0) { |1, 2| 1 + 2 }
Schritt 3
[1, 2, 3, 4].inject(0) { |3, 3| 3 + 3 }
Schritt 4
[1, 2, 3, 4].inject(0) { |6, 4| 6 + 4 }
Schritt: 5
[1, 2, 3, 4].inject(0) { |10, Now no elements left in the array, so it'll return 10 from this step| }
Hier sind fett-kursive Werte Elemente, die aus dem Array abgerufen werden, und die einfach fett gedruckten Werte sind die resultierenden Werte.
Ich hoffe, dass Sie die Funktionsweise der
#inject
Methode der verstehen#ruby
.quelle
Der Code durchläuft die vier Elemente im Array und fügt dem aktuellen Element das vorherige Ergebnis hinzu:
quelle
Was sie gesagt haben, aber beachten Sie auch, dass Sie nicht immer einen "Startwert" angeben müssen:
ist das gleiche wie
Probieren Sie es aus, ich werde warten.
Wenn kein Argument zum Injizieren übergeben wird, werden die ersten beiden Elemente an die erste Iteration übergeben. Im obigen Beispiel ist das Ergebnis 1 und das Element beim ersten Mal 2, sodass der Block weniger aufgerufen wird.
quelle
Die Zahl, die Sie in Ihr () der Injektion eingeben, stellt einen Startplatz dar. Sie kann 0 oder 1000 sein. In den Rohren befinden sich zwei Platzhalter | x, y |. x = welche Zahl auch immer Sie in der .inject ('x') hatten, und die Sekunde repräsentiert jede Iteration Ihres Objekts.
[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15
quelle
Inject wendet den Block an
zu jedem Element im Array. Für das nächste Element ("Element") lautet der vom Block zurückgegebene Wert "Ergebnis". So wie Sie es genannt haben (mit einem Parameter), beginnt "Ergebnis" mit dem Wert dieses Parameters. Der Effekt addiert also die Elemente.
quelle
tldr;
inject
unterscheidet sichmap
in einem wichtigen Punkt:inject
Gibt den Wert der letzten Ausführung des Blocks zurück, währendmap
das Array zurückgegeben wird, über das iteriert wurde.Darüber hinaus wurde der Wert jeder Blockausführung über den ersten Parameter (
result
in diesem Fall) an die nächste Ausführung übergeben, und Sie können diesen Wert (das(0)
Teil) initialisieren .Ihr obiges Beispiel könnte folgendermaßen geschrieben werden
map
:Gleicher Effekt, aber
inject
hier prägnanter.Sie werden häufig feststellen, dass eine Zuweisung im
map
Block erfolgt, während eine Auswertung iminject
Block erfolgt.Welche Methode Sie wählen, hängt vom gewünschten Umfang ab
result
. Wann man es nicht benutzt, wäre ungefähr so:Sie mögen wie alle sein: "Sieh mal, ich habe das alles nur in einer Zeile zusammengefasst", aber Sie haben auch vorübergehend Speicher
x
alsresult
Arbeitsvariable zugewiesen , die nicht erforderlich war, da Sie bereits damit arbeiten mussten.quelle
ist gleichbedeutend mit:
quelle
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10
Im Klartext durchlaufen Sie dieses Array (
[1,2,3,4]
). Sie werden dieses Array viermal durchlaufen, da es vier Elemente gibt (1, 2, 3 und 4). Die Inject-Methode hat 1 Argument (die Nummer 0), und Sie fügen dieses Argument dem 1. Element hinzu (0 + 1. Dies entspricht 1). 1 wird im "Ergebnis" gespeichert. Dann addieren Sie dieses Ergebnis (das 1 ist) zum nächsten Element (1 + 2. Dies ist 3). Dies wird nun als Ergebnis gespeichert. Mach weiter: 3 + 3 ist gleich 6. Und schließlich ist 6 + 4 gleich 10.quelle
Dieser Code erlaubt nicht die Möglichkeit, keinen Startwert zu übergeben, kann aber helfen, zu erklären, was los ist.
quelle
Beginnen Sie hier und überprüfen Sie dann alle Methoden, die Blöcke benötigen. http://ruby-doc.org/core-2.3.3/Enumerable.html#method-i-inject
Ist es der Block, der Sie verwirrt oder warum Sie einen Wert in der Methode haben? Gute Frage. Was ist die Operatormethode dort?
Wie fängt es an?
Können wir das machen?
Funktioniert das?
Sie sehen, ich baue auf der Idee auf, dass es einfach alle Elemente des Arrays summiert und eine Zahl in dem Memo ergibt, das Sie in den Dokumenten sehen.
Sie können dies immer tun
um zu sehen, wie die Aufzählung des Arrays durchlaufen wird. Das ist die Grundidee.
Es ist nur so, dass Sie durch Injizieren oder Reduzieren ein Memo oder einen Akku erhalten, der verschickt wird.
Wir könnten versuchen, ein Ergebnis zu erzielen
aber nichts kommt zurück, so dass dies genauso verhält wie zuvor
im Elementinspektorblock.
quelle
Dies ist eine einfache und ziemlich leicht verständliche Erklärung:
Vergessen Sie den "Anfangswert", da er am Anfang etwas verwirrend ist.
Sie können das Obige wie folgt verstehen: Ich injiziere eine "Addiermaschine" zwischen 1,2,3,4. Das heißt, es ist 1 ♫ 2 ♫ 3 ♫ 4 und ♫ ist eine Addiermaschine, also ist es dasselbe wie 1 + 2 + 3 + 4 und es ist 10.
Sie können tatsächlich ein
+
dazwischen spritzen :und es ist so, als würde man ein
+
zwischen 1,2,3,4 injizieren , was es zu 1 + 2 + 3 + 4 macht und es ist 10. Das:+
ist Rubys Art,+
in Form eines Symbols zu spezifizieren .Dies ist recht einfach zu verstehen und intuitiv. Und wenn Sie Schritt für Schritt analysieren möchten, wie es funktioniert, ist es wie folgt: Nehmen Sie 1 und 2 und fügen Sie sie jetzt hinzu. Wenn Sie ein Ergebnis haben, speichern Sie es zuerst (das ist 3), und jetzt wird das nächste gespeichert Wert 3 und das Array-Element 3 durchlaufen den a + b-Prozess, der 6 ist, und speichern jetzt diesen Wert, und jetzt durchlaufen 6 und 4 den a + b-Prozess und sind 10. Sie tun dies im Wesentlichen
und ist 10. Der "Anfangswert"
0
ist zunächst nur eine "Basis". In vielen Fällen brauchen Sie es nicht. Stellen Sie sich vor, Sie brauchen 1 * 2 * 3 * 4 und es istund es ist geschafft. Sie brauchen keinen "Anfangswert" von
1
, um das Ganze mit zu multiplizieren1
.quelle
Es gibt eine andere Form der .inject () -Methode, die sehr hilfreich ist [4,5] .inject (&: +), die das gesamte Element des Bereichs addiert
quelle
Es ist nur
reduce
oderfold
, wenn Sie mit anderen Sprachen vertraut sind.quelle
Ist das gleiche wie das:
quelle