Was ist der Unterschied zwischen einer Coroutine und einer Fortsetzung und einem Generator?
Was ist der Unterschied zwischen einer Coroutine und einer Fortsetzung und einem Generator?
Antworten:
Ich werde mit Generatoren beginnen, da dies der einfachste Fall ist. Wie @zvolkov erwähnt hat, handelt es sich um Funktionen / Objekte, die wiederholt aufgerufen werden können, ohne dass sie zurückgegeben werden. Wenn sie jedoch aufgerufen werden, wird ein Wert zurückgegeben (erhalten) und die Ausführung wird dann ausgesetzt. Wenn sie erneut angerufen werden, starten sie dort, wo sie die Hinrichtung zuletzt ausgesetzt haben, und machen ihr Ding erneut.
Ein Generator ist im Wesentlichen eine abgeschnittene (asymmetrische) Coroutine. Der Unterschied zwischen einer Coroutine und einem Generator besteht darin, dass eine Coroutine Argumente akzeptieren kann, nachdem sie ursprünglich aufgerufen wurde, während ein Generator dies nicht kann.
Es ist ein bisschen schwierig, ein triviales Beispiel dafür zu finden, wo Sie Coroutinen verwenden würden, aber hier ist mein bester Versuch. Nehmen Sie diesen (erfundenen) Python-Code als Beispiel.
def my_coroutine_body(*args):
while True:
# Do some funky stuff
*args = yield value_im_returning
# Do some more funky stuff
my_coro = make_coroutine(my_coroutine_body)
x = 0
while True:
# The coroutine does some funky stuff to x, and returns a new value.
x = my_coro(x)
print x
Ein Beispiel für die Verwendung von Coroutinen sind Lexer und Parser. Ohne Coroutinen in der Sprache oder irgendwie emuliert, müssen Lexing- und Parsing-Code miteinander gemischt werden, obwohl es sich tatsächlich um zwei separate Probleme handelt. Mit einer Coroutine können Sie den Lexing- und Parsing-Code jedoch trennen.
(Ich werde den Unterschied zwischen symmetrischen und asymmetrischen Coroutinen überarbeiten. Es genügt zu sagen, dass sie gleichwertig sind, Sie können von einem zum anderen konvertieren, und asymmetrische Coroutinen - die den Generatoren am ähnlichsten sind - sind die leichter zu verstehen. Ich habe skizziert, wie man asymmetrische Coroutinen in Python implementieren könnte.)
Fortsetzungen sind eigentlich ganz einfache Bestien. Alles, was sie sind, sind Funktionen, die einen anderen Punkt im Programm darstellen. Wenn Sie ihn aufrufen, wechselt die Ausführung automatisch zu dem Punkt, den diese Funktion darstellt. Sie verwenden jeden Tag sehr eingeschränkte Versionen davon, ohne es zu merken. Ausnahmen können zum Beispiel als eine Art Inside-Out-Fortsetzung betrachtet werden. Ich werde Ihnen ein Python-basiertes Pseudocode-Beispiel für eine Fortsetzung geben.
Angenommen, Python hat eine Funktion aufgerufen callcc()
, und diese Funktion hat zwei Argumente verwendet, wobei das erste eine Funktion und das zweite eine Liste von Argumenten ist, mit denen sie aufgerufen werden soll. Die einzige Einschränkung für diese Funktion wäre, dass das letzte Argument eine Funktion ist (die unsere aktuelle Fortsetzung sein wird).
def foo(x, y, cc):
cc(max(x, y))
biggest = callcc(foo, [23, 42])
print biggest
Was passieren würde, wäre, dass callcc()
dies wiederum foo()
mit der aktuellen Fortsetzung ( cc
) aufgerufen würde, dh einem Verweis auf den Punkt im Programm, an dem callcc()
aufgerufen wurde. Wenn Sie foo()
die aktuelle Fortsetzung callcc()
aufrufen , entspricht dies im Wesentlichen der Aufforderung, mit dem Wert zurückzukehren, mit dem Sie die aktuelle Fortsetzung aufrufen. Wenn dies der Fall ist, wird der Stapel an den Ort zurückgesetzt, an dem die aktuelle Fortsetzung erstellt wurde, dh wenn Sie aufgerufen haben callcc()
.
Das Ergebnis all dessen wäre, dass unsere hypothetische Python-Variante gedruckt würde '42'
.
Ich hoffe, das hilft, und ich bin sicher, dass meine Erklärung einiges verbessert werden kann!
Coroutine ist eine von mehreren Prozeduren, die abwechselnd ihre Arbeit erledigen und dann pausieren, um den anderen Coroutinen in der Gruppe die Kontrolle zu geben.
Die Fortsetzung ist ein "Zeiger auf eine Funktion", die Sie an eine Prozedur übergeben, die ausgeführt wird ("Fortsetzung mit"), wenn diese Prozedur abgeschlossen ist.
Generator (in .NET) ist ein Sprachkonstrukt, das einen Wert ausspucken, die Ausführung der Methode "pausieren" und dann an derselben Stelle fortfahren kann, wenn Sie nach dem nächsten Wert gefragt werden.
In einer neueren Version von Python können Sie Werte an Generatoren mit senden generator.send()
, wodurch Python-Generatoren effektiv Coroutinen erstellen.
Der Hauptunterschied zwischen dem Python-Generator und einem anderen Generator, z. B. Greenlet, besteht darin, dass Sie in Python yield value
nur zum Aufrufer zurückkehren können. Während Sie sich im Greenlet befinden, target.switch(value)
können Sie zu einer bestimmten Zielkoroutine gelangen und einen Wert erhalten, bei dem target
die weiterhin ausgeführt wird.
yield
müssen sich alle Aufrufe in derselben Funktion befinden, die als "Generator" bezeichnet wird. Sie können nicht yield
von einer Unterfunktion, weshalb Pythons Semi-Coroutinen genannt werden , während Lua asymmetrische Coroutinen hat . (Es gibt Vorschläge, um die Erträge zu verbreiten, aber ich denke, diese