Ich versuche zu verstehen, yield
wie Blöcke funktionieren und wie sie in Ruby funktionieren.
Wie wird yield
verwendet? Viele der Rails-Anwendungen, die ich mir angesehen habe, werden auf yield
seltsame Weise verwendet.
Kann mir jemand erklären oder mir zeigen, wohin ich gehen soll, um sie zu verstehen?
Antworten:
Ja, es ist zunächst ein bisschen rätselhaft.
In Ruby können Methoden einen Codeblock empfangen, um beliebige Codesegmente auszuführen.
Wenn eine Methode einen Block erwartet, ruft sie ihn durch Aufrufen der
yield
Funktion auf.Dies ist beispielsweise sehr praktisch, um eine Liste zu durchlaufen oder einen benutzerdefinierten Algorithmus bereitzustellen.
Nehmen Sie das folgende Beispiel:
Ich werde eine
Person
Klasse definieren, die mit einem Namen initialisiert wurde, und einedo_with_name
Methode bereitstellen , die beim Aufrufen nur dasname
Attribut an den empfangenen Block übergibt.Dies würde es uns ermöglichen, diese Methode aufzurufen und einen beliebigen Codeblock zu übergeben.
Um beispielsweise den Namen zu drucken, würden wir Folgendes tun:
Würde drucken:
Beachten Sie, dass der Block als Parameter eine Variable mit dem Namen empfängt (Hinweis:
name
Sie können diese Variable beliebig aufrufen, es ist jedoch sinnvoll, sie aufzurufenname
). Wenn der Code aufgerufen wirdyield
, füllt er diesen Parameter mit dem Wert von@name
.Wir könnten einen weiteren Block bereitstellen, um eine andere Aktion auszuführen. Kehren Sie beispielsweise den Namen um:
Wir haben genau die gleiche Methode (
do_with_name
) verwendet - es ist nur ein anderer Block.Dieses Beispiel ist trivial. Interessantere Anwendungen sind das Filtern aller Elemente in einem Array:
Oder wir können auch einen benutzerdefinierten Sortieralgorithmus bereitstellen, der beispielsweise auf der Zeichenfolgengröße basiert:
Ich hoffe, das hilft Ihnen, es besser zu verstehen.
Übrigens, wenn der Block optional ist, sollten Sie ihn wie folgt aufrufen:
Wenn dies nicht optional ist, rufen Sie es einfach auf.
BEARBEITEN
@hmak hat ein repl.it für diese Beispiele erstellt: https://repl.it/@makstaks/blocksandyieldsrubyexample
quelle
racsO
wennthe_name = ""
"Oscar"
(ist in der Antwort nicht sehr klar)person.do_with_name {|string| yield string, something_else }
In Ruby können Methoden überprüfen, ob sie so aufgerufen wurden, dass zusätzlich zu den normalen Argumenten ein Block bereitgestellt wurde. In der Regel erfolgt dies mit der
block_given?
Methode. Sie können den Block jedoch auch als expliziten Proc bezeichnen, indem Sie&
vor dem endgültigen Argumentnamen ein kaufmännisches Und ( ) voranstellen .Wenn eine Methode mit einem Block aufgerufen wird, kann die Methode
yield
bei Bedarf mit einigen Argumenten den Block steuern (den Block aufrufen). Betrachten Sie diese Beispielmethode, die Folgendes demonstriert:Oder verwenden Sie die spezielle Blockargument-Syntax:
quelle
Es ist durchaus möglich, dass hier jemand eine wirklich detaillierte Antwort gibt, aber ich habe diesen Beitrag von Robert Sosinski immer als eine großartige Erklärung für die Feinheiten zwischen Blöcken, Prozessen und Lambdas angesehen.
Ich sollte hinzufügen, dass ich glaube, dass der Beitrag, auf den ich verlinke, spezifisch für Ruby 1.8 ist. Einige Dinge haben sich in Ruby 1.9 geändert, z. B. Blockvariablen, die lokal für den Block sind. In 1.8 erhalten Sie ungefähr Folgendes:
Während 1.9 Ihnen geben würde:
Ich habe nicht 1.9 auf diesem Computer, daher kann ein Fehler auftreten.
quelle
Ich wollte irgendwie hinzufügen, warum Sie die bereits so guten Antworten so machen würden.
Keine Ahnung, aus welcher Sprache Sie kommen, aber wenn es sich um eine statische Sprache handelt, wird Ihnen so etwas bekannt vorkommen. So lesen Sie eine Datei in Java
Die ganze Sache ist, die Idee zu ignorieren
So machst du es in Rubin
Ganz anders. Brechen Sie dieses auf
Anstatt die Schritte eins und zwei zu behandeln, delegieren Sie dies im Grunde genommen an eine andere Klasse. Wie Sie sehen können, wird dadurch die Menge an Code, die Sie schreiben müssen, drastisch reduziert, was das Lesen erleichtert und die Wahrscheinlichkeit verringert, dass beispielsweise Speicherlecks oder Dateisperren nicht gelöscht werden.
Nun, es ist nicht so, dass man in Java nichts Ähnliches machen kann, tatsächlich machen es die Leute schon seit Jahrzehnten. Es heißt das Strategiemuster . Der Unterschied besteht darin, dass ohne Blöcke für etwas Einfaches wie das Dateibeispiel die Strategie aufgrund der Anzahl der Klassen und Methoden, die Sie schreiben müssen, übertrieben wird. Mit Blöcken ist dies eine so einfache und elegante Methode, dass es keinen Sinn macht, Ihren Code NICHT so zu strukturieren.
Dies ist nicht die einzige Möglichkeit, wie Blöcke verwendet werden, aber die anderen (wie das Builder-Muster, das Sie in form_for api in Rails sehen können) sind ähnlich genug, dass es offensichtlich sein sollte, was passiert, wenn Sie Ihren Kopf darum wickeln. Wenn Sie Blöcke sehen, können Sie normalerweise davon ausgehen, dass der Methodenaufruf genau das ist, was Sie tun möchten, und der Block beschreibt, wie Sie ihn ausführen möchten.
quelle
File.readlines("readfile.rb").each_with_index do |line, index| puts "#{index + 1}: #{line}" end
und die Java-Leute noch härter auslachen.IO.foreach('readfile.rb').each_with_index { |line, index| puts "#{index}: #{line}" }
(plus keine Speicherprobleme)Ich fand diesen Artikel sehr nützlich. Insbesondere das folgende Beispiel:
welches die folgende Ausgabe geben sollte:
Im Wesentlichen
yield
wird der Code bei jedem Aufruf von Ruby imdo
Block oder im Inneren ausgeführt{}
. Wenn ein Parameter für bereitgestellt wird,yield
wird dieser als Parameter für dendo
Block bereitgestellt .Für mich war es das erste Mal, dass ich wirklich verstand, was die
do
Blöcke taten. Es ist im Grunde eine Möglichkeit für die Funktion, Zugriff auf interne Datenstrukturen zu gewähren, sei es für die Iteration oder für die Konfiguration der Funktion.Wenn Sie also in Schienen sind, schreiben Sie Folgendes:
Dadurch wird die
respond_to
Funktion ausgeführt, die dendo
Block mit dem (internen)format
Parameter ergibt . Sie rufen dann die.html
Funktion für diese interne Variable auf, die wiederum den Codeblock zum Ausführen desrender
Befehls ergibt . Beachten Sie, dass dies.html
nur dann der Fall ist, wenn es sich um das angeforderte Dateiformat handelt. (Technische Daten: Diese Funktionen werden tatsächlichblock.call
nicht verwendet,yield
wie Sie aus der Quelle ersehen können, aber die Funktionalität ist im Wesentlichen dieselbe . Weitere Informationen finden Sie in dieser Frage .) Auf diese Weise kann die Funktion eine Initialisierung durchführen und dann Eingaben aus dem aufrufenden Code und vornehmen dann bei Bedarf weiter verarbeiten.Oder anders ausgedrückt, es ähnelt einer Funktion, die eine anonyme Funktion als Argument verwendet und sie dann in Javascript aufruft.
quelle
In Ruby ist ein Block im Grunde ein Codeabschnitt, der an jede Methode übergeben und von dieser ausgeführt werden kann. Blöcke werden immer mit Methoden verwendet, die ihnen normalerweise Daten (als Argumente) zuführen.
Blöcke werden häufig in Ruby-Edelsteinen (einschließlich Rails) und in gut geschriebenem Ruby-Code verwendet. Sie sind keine Objekte und können daher keinen Variablen zugewiesen werden.
Grundlegende Syntax
Ein Block ist ein Code, der von {} oder do..end eingeschlossen ist. Konventionell sollte die geschweifte Klammer-Syntax für einzeilige Blöcke und die do..end-Syntax für mehrzeilige Blöcke verwendet werden.
Jede Methode kann einen Block als implizites Argument empfangen. Ein Block wird von der Yield-Anweisung innerhalb einer Methode ausgeführt. Die grundlegende Syntax lautet:
Wenn die Yield-Anweisung erreicht ist, gibt die Meditationsmethode dem Block die Kontrolle, der Code innerhalb des Blocks wird ausgeführt und die Kontrolle wird an die Methode zurückgegeben, die die Ausführung unmittelbar nach der Yield-Anweisung wieder aufnimmt.
Wenn eine Methode eine Yield-Anweisung enthält, erwartet sie beim Aufruf einen Block. Wenn kein Block bereitgestellt wird, wird eine Ausnahme ausgelöst, sobald die Yield-Anweisung erreicht ist. Wir können den Block optional machen und vermeiden, dass eine Ausnahme ausgelöst wird:
Es ist nicht möglich, mehrere Blöcke an eine Methode zu übergeben. Jede Methode kann nur einen Block empfangen.
Weitere Informationen finden Sie unter: http://www.zenruby.info/2016/04/introduction-to-blocks-in-ruby.html
quelle
Ich benutze manchmal "Ausbeute" wie folgt:
quelle
Logger
keine Aufgabe ausführen muss, wenn der Benutzer dies nicht muss. Sie sollten Ihre jedoch erklären ...Erträge, um es einfach auszudrücken, erlauben der von Ihnen erstellten Methode, Blöcke zu nehmen und aufzurufen. Das Yield-Schlüsselwort ist speziell die Stelle, an der das 'Zeug' im Block ausgeführt wird.
quelle
Es gibt zwei Punkte, die ich hier zum Ertrag ansprechen möchte. Während viele Antworten hier über verschiedene Möglichkeiten sprechen, einen Block an eine Methode zu übergeben, die Yield verwendet, sprechen wir zunächst auch über den Kontrollfluss. Dies ist besonders relevant, da Sie einem Block MEHRERE Zeiten geben können. Schauen wir uns ein Beispiel an:
Wenn jede Methode aufgerufen wird, wird sie zeilenweise ausgeführt. Wenn wir nun zum 3.times-Block kommen, wird dieser Block dreimal aufgerufen. Jedes Mal, wenn es Ertrag aufruft. Diese Ausbeute ist mit dem Block verknüpft, der der Methode zugeordnet ist, die die einzelnen Methoden aufgerufen hat. Es ist wichtig zu beachten, dass jedes Mal, wenn Yield aufgerufen wird, die Kontrolle an den Block jeder Methode im Clientcode zurückgegeben wird. Sobald die Ausführung des Blocks abgeschlossen ist, kehrt er zum 3.times-Block zurück. Und das passiert dreimal. Dieser Block im Clientcode wird also dreimal aufgerufen, da Yield explizit dreimal hintereinander aufgerufen wird.
Mein zweiter Punkt betrifft enum_for und Yield. enum_for instanziiert die Enumerator-Klasse und dieses Enumerator-Objekt reagiert auch auf Yield.
Beachten Sie also, dass jedes Mal, wenn wir Arten mit dem externen Iterator aufrufen, die Ausbeute nur einmal aufgerufen wird. Wenn wir es das nächste Mal aufrufen, wird die nächste Ausbeute aufgerufen und so weiter.
Es gibt einen interessanten Leckerbissen in Bezug auf enum_for. In der Online-Dokumentation heißt es:
Wenn Sie für enum_for kein Symbol als Argument angeben, verknüpft ruby den Enumerator mit der jeweiligen Methode des Empfängers. Einige Klassen haben nicht jede Methode, wie die String-Klasse.
Bei einigen Objekten, die mit enum_for aufgerufen werden, müssen Sie daher explizit angeben, wie Ihre Aufzählungsmethode aussehen soll.
quelle
Yield kann als namenloser Block verwendet werden, um einen Wert in der Methode zurückzugeben. Betrachten Sie den folgenden Code:
Sie können eine Methode "Up" erstellen, der ein Argument zugewiesen ist. Sie können dieses Argument jetzt dem Ertrag zuweisen, der einen zugeordneten Block aufruft und ausführt. Sie können den Block nach der Parameterliste zuweisen.
Wenn die Up-Methode Ausbeute mit einem Argument aufruft, wird sie an die Blockvariable übergeben, um die Anforderung zu verarbeiten.
quelle