Coroutine vs Continuazione vs Generatore


147

Qual è la differenza tra una coroutine e una continuazione e un generatore?


2
Mi chiedo se le coroutine e le continuazioni siano effettivamente equivalenti. So che è possibile modellare le coroutine con continuazioni, ma è possibile modellare le continuazioni con coroutine o no perché le continuazioni sono strettamente più potenti?
nalply

Risposte:


127

Inizierò con i generatori, visto che sono il caso più semplice. Come accennato da @zvolkov, sono funzioni / oggetti che possono essere ripetutamente richiamati senza tornare, ma quando vengono richiamati restituiscono (producono) un valore e quindi sospendono la loro esecuzione. Quando vengono chiamati di nuovo, si avviano da dove hanno sospeso l'ultima esecuzione e fanno di nuovo le loro cose.

Un generatore è essenzialmente una coroutine ridotta (asimmetrica). La differenza tra una coroutine e un generatore è che una coroutine può accettare argomenti dopo essere stata inizialmente chiamata, mentre un generatore non può.

È un po 'difficile trovare un esempio banale di dove useresti le coroutine, ma ecco il mio miglior tentativo. Prendi questo (inventato) codice Python come esempio.

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

Un esempio di utilizzo delle coroutine sono i lexer e i parser. Senza coroutine nella lingua o emulati in qualche modo, il lexing e il codice di analisi devono essere mescolati insieme anche se in realtà sono due preoccupazioni separate. Ma usando una coroutine, puoi separare il codice lexing e di analisi.

(Vado a ripassare la differenza tra coroutine simmetriche e asimmetriche. Basti dire che sono equivalenti, puoi convertire da una all'altra, e le coroutine asimmetriche - che sono i generatori più simili - sono le più facile da capire. Stavo delineando come si potrebbero implementare coroutine asimmetriche in Python.)

Le continuazioni sono in realtà bestie piuttosto semplici. Tutto ciò che sono, sono funzioni che rappresentano un altro punto nel programma che, se lo chiamate, farà sì che l'esecuzione passi automaticamente al punto rappresentato dalla funzione. Ne usi versioni molto limitate ogni giorno senza nemmeno accorgertene. Le eccezioni, ad esempio, possono essere pensate come una sorta di continuazione dentro e fuori. Ti darò un esempio di pseudocodice basato su Python di una continuazione.

Supponiamo che Python abbia una funzione chiamata callcc(), e questa funzione prende due argomenti, il primo è una funzione e il secondo è un elenco di argomenti con cui chiamarlo. L'unica restrizione a quella funzione sarebbe che l'ultimo argomento che prenderà sarà una funzione (che sarà la nostra continuazione attuale).

def foo(x, y, cc):
   cc(max(x, y))

biggest = callcc(foo, [23, 42])
print biggest

Ciò che accadrebbe è che callcc()a sua volta chiamerebbe foo()con l'attuale continuazione ( cc), cioè un riferimento al punto nel programma in cui è callcc()stato chiamato. Quando foo()chiama la continuazione corrente, è essenzialmente lo stesso che dire callcc()di restituire con il valore con cui stai chiamando la continuazione corrente, e quando lo fa, riporta lo stack al punto in cui è stata creata la continuazione corrente, cioè quando hai chiamato callcc().

Il risultato di tutto ciò sarebbe che la nostra ipotetica variante di Python sarebbe stampata '42'.

Spero che sia di aiuto, e sono sicuro che la mia spiegazione può essere migliorata parecchio!


6
Un aspetto: le continuazioni delimitate sono funzioni, ma le continuazioni non delimitate
Frank Shearar

2
È un buon punto. Detto questo, nella maggior parte delle applicazioni pratiche, quando le persone dicono "continuazione", stanno parlando di continuazioni parziali / delimitate. L'inserimento di vari altri tipi di continuazioni avrebbe confuso in qualche modo la spiegazione.
Keith Gaughan,

1
Le continuazioni non sono funzioni, sebbene possano essere reificate in funzioni. "Detto questo, nella maggior parte delle applicazioni pratiche, quando le persone dicono" continuazione ", stanno parlando di continuazioni parziali / delimitate." Indichi tale uso del termine "continuazione"? Non ho mai incontrato un tale utilizzo. Inoltre hai fornito un esempio per una continuazione non delimitata, usando call / cc. Gli operatori per le continuazioni delimitate vengono generalmente "ripristinati" e "shift" (possono avere altri nomi).
Ivancho,

3
Cominciamo dal fatto che sono passati cinque anni da quando ho scritto questo. Sei in ritardo alla festa. In secondo luogo, so che le continuazioni non delimitate non sono funzioni, ma tu riguardo a te provi a spiegare come funzionano senza fare riferimento a loro come tali, mantenendo allo stesso tempo il linguaggio semplice. Dal punto di vista del programmatore medio, il fatto che una continuazione non delimitata non ritorni lo rende solo una funzione one-shot, che non è corretta secondo la definizione di cosa sia una funzione, ma almeno è comprensibile .
Keith Gaughan,

2
Non sono in ritardo per la festa poiché questo è il primo risultato che ottengo su Google quando cerco "coroutine vs generator". Speravo di trovare alcune buone informazioni sulle loro differenze. Comunque l'ho trovato altrove. E non sono il primo a sottolineare che la tua spiegazione sulle continuazioni è sbagliata. Il problema è che qualcuno lo sbaglierà e potrebbe essere confuso in seguito quando incontra la stessa parola usata per qualcosa di diverso.
Ivancho,

33

La coroutine è una delle diverse procedure che a turno svolgono il loro lavoro e poi si fermano per dare il controllo alle altre coroutine del gruppo.

La continuazione è un "puntatore a una funzione" che si passa a una procedura, da eseguire ("continua con") al termine di tale procedura.

Generator (in .NET) è un costrutto di linguaggio che può sputare un valore, "mettere in pausa" l'esecuzione del metodo e quindi procedere dallo stesso punto quando viene richiesto il valore successivo.


Mi rendo conto che la risposta potrebbe non essere precisa, ma a questo livello di domanda ho provato a mantenerla semplice. Inoltre, non capisco tutto da solo :)
zvolkov,

Un generatore in Python è simile alla versione C #, ma è implementato come una sintassi speciale per la creazione di un'istanza di un oggetto iteratore, che restituisce i valori restituiti dalla definizione "funzione" fornita.
Benson,

2
Una piccola correzione: "... incluso lo stack di chiamate e tutte le variabili MA NON I LORO VALORI" (o semplicemente rilascia "tutte le variabili"). Le continuazioni non conservano i valori, contengono semplicemente lo stack di chiamate.
inalterato il

No, le continuazioni non sono "puntatore a una funzione". Nell'implementazione più ingenua, contiene un puntatore alla funzione e un ambiente contiene le variabili locali. E non ritorna mai a meno che tu non usi qualcosa come call / cc per catturarlo con un valore di ritorno.
NalaGinrut,

9

Nella versione più recente di Python, è possibile inviare valori a Generatori con generator.send(), il che rende i generatori di Python effettivamente coroutine.

La differenza principale tra python Generator e altri generatori, diciamo greenlet, è che in python, yield valuepuoi solo tornare al chiamante. Mentre sei in greenlet, target.switch(value)puoi portarti a un coroutine target specifico e produrre un valore in cui targetcontinuerebbe a funzionare.


3
Ma in Python, tutte le yieldchiamate devono essere nella stessa funzione, che si chiama "Generatore". Non è possibile utilizzare yielduna sottofunzione, motivo per cui quelli di Python sono chiamati semi-coroutine , mentre Lua ha coroutine asimmetriche . (Ci sono proposte per propagare i raccolti, ma penso che siano solo fangose ​​le acque.)
cdunn2001,

7
@ cdunn2001: (commento di Winston) Python3.3 ha introdotto l'espressione "cedere da" che ti ha permesso di cedere dal sottogeneratore.
Linus Caldwell,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.