web-dev-qa-db-fra.com

Coroutine vs Continuation vs Générateur

Quelle est la différence entre une coroutine et une continuation et un générateur?

136
Mehdi Asgari

Je vais commencer par les générateurs, car c'est le cas le plus simple. Comme @zvolkov l'a mentionné, ce sont des fonctions/objets qui peuvent être appelés à plusieurs reprises sans retourner, mais lorsqu'ils sont appelés, ils renverront (donneront) une valeur, puis suspendront leur exécution. Quand ils seront rappelés, ils recommenceront depuis leur dernière suspension d'exécution et recommenceront.

Un générateur est essentiellement une coroutine coupée (asymétrique). La différence entre une coroutine et un générateur est qu'une coroutine peut accepter des arguments après son premier appel, alors qu'un générateur ne peut pas.

Il est un peu difficile de trouver un exemple trivial de l'endroit où vous utiliseriez des coroutines, mais voici mon meilleur essai. Prenez ceci (composé) Python comme exemple.

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 exemple d'utilisation de coroutines est les lexers et les parseurs. Sans coroutines dans le langage ou émulé d'une manière ou d'une autre, le code de lexing et d'analyse doit être mélangé, même s'il s'agit en réalité de deux préoccupations distinctes. Mais en utilisant une coroutine, vous pouvez séparer le code de lexing et d'analyse.

(Je vais brosser la différence entre les coroutines symétriques et asymétriques. Il suffit de dire qu'elles sont équivalentes, vous pouvez convertir de l'une à l'autre, et les coroutines asymétriques - qui sont les plus comme des générateurs - sont les plus facile à comprendre. Je décrivais comment on pourrait implémenter des coroutines asymétriques en Python.)

Les continuations sont en fait des bêtes assez simples. Tout ce qu'ils sont, ce sont des fonctions représentant un autre point du programme qui, si vous l'appelez, feront automatiquement passer l'exécution au point que cette fonction représente. Vous en utilisez quotidiennement des versions très restreintes sans même vous en rendre compte. Les exceptions, par exemple, peuvent être considérées comme une sorte de continuation à l'envers. Je vais vous donner un Python exemple pseudocode basé sur une continuation.

Dites Python avait une fonction appelée callcc(), et cette fonction prenait deux arguments, le premier étant une fonction, et le second étant une liste d'arguments pour l'appeler. la seule restriction sur cette fonction serait que le dernier argument qu'il prendra sera une fonction (qui sera notre suite actuelle).

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

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

Ce qui se passerait, c'est que callcc() appelerait à son tour foo() avec la continuation actuelle (cc), c'est-à-dire une référence au point du programme auquel callcc() a été appelée. Lorsque foo() appelle la continuation actuelle, c'est essentiellement la même chose que de dire à callcc() de retourner avec la valeur avec laquelle vous appelez la continuation actuelle, et quand elle le fait, elle annule la pile à l'endroit où la continuation actuelle a été créée, c'est-à-dire lorsque vous avez appelé callcc().

Le résultat de tout cela serait que notre hypothétique Python afficherait '42'.

J'espère que cela aide, et je suis sûr que mon explication peut être améliorée un peu!

118
Keith Gaughan

La coroutine est l'une des nombreuses procédures qui font leur travail à tour de rôle, puis s'arrêtent pour donner le contrôle aux autres coroutines du groupe.

La suite est un "pointeur vers une fonction" que vous passez à une procédure, à exécuter ("suite avec") lorsque cette procédure est terminée.

Le générateur (en .NET) est une construction de langage qui peut cracher une valeur, "suspendre" l'exécution de la méthode, puis procéder à partir du même point lorsqu'on lui demande la valeur suivante.

32
zvolkov

Dans une version plus récente de Python, vous pouvez envoyer des valeurs aux générateurs avec generator.send(), ce qui fait que les générateurs python) coroutinent efficacement.

La principale différence entre python Generator et un autre générateur, disons greenlet) est qu'en python, votre yield value Ne peut que retourner à l'appelant. En greenlet, target.switch(value) peut vous amener à une coroutine cible spécifique et produire une valeur où target continuerait de s'exécuter.

8
Yichuan Wang