web-dev-qa-db-ja.com

コルーチンvs継続vsジェネレーター

コルーチンと継続とジェネレーターの違いは何ですか?

136
Mehdi Asgari

ジェネレーターから始めましょう。ジェネレーターが最も単純なケースであるためです。 @zvolkovが述べたように、これらは戻ることなく繰り返し呼び出すことができる関数/オブジェクトですが、呼び出されると値を返し(yield)、実行を中断します。再び呼び出されると、最後に実行を中断した場所から起動し、再度実行します。

ジェネレーターは、本質的にカットダウン(非対称)コルーチンです。コルーチンとジェネレーターの違いは、コルーチンが最初に呼び出された後に引数を受け入れることができるのに対し、ジェネレーターはできないことです。

コルーチンをどこで使用するかという些細な例を思いつくのは少し難しいですが、ここに私の最善の試みを示します。例として、これ(構成)Pythonコードを取り上げます。

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

コルーチンが使用される場所の例は、レクサーとパーサーです。言語にコルーチンやエミュレートされていない場合、実際には2つの別個の関心事であるにもかかわらず、字句解析と解析のコードを混在させる必要があります。しかし、コルーチンを使用すると、字句解析コードと解析コードを分離できます。

(対称コルーチンと非対称コルーチンの違いについて説明します。これらは同等であると言えば十分です。一方から他方に変換でき、非対称コルーチン(ジェネレーターに最も似ています)はPythonで非対称コルーチンを実装する方法を概説していました。)

継続は実際には非常に単純な獣です。それらはすべて、プログラム内の別のポイントを表す関数です。この関数を呼び出すと、その関数が表すポイントに実行が自動的に切り替わります。気づかないうちに、非常に制限されたバージョンを毎日使用しています。たとえば、例外は一種の裏返しの継続と考えることができます。 Pythonベースの継続の擬似コードの例を示します。

Pythonにはcallcc()という関数があり、この関数は2つの引数を取りました。1つ目は関数で、2つ目はそれを呼び出す引数のリストです。唯一の制限その関数では、最後の引数が関数になります(これが現在の続きです)。

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

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

起こるのは、callcc()が現在の継続(cc)でfoo()を呼び出すことです。つまり、callcc()が存在するプログラム内のポイントへの参照です。と呼ばれていました。 foo()が現在の継続を呼び出すとき、それはcallcc()に現在の継続を呼び出している値を返すように指示するのと本質的に同じであり、それを行うと、スタックを現在の継続が作成されました。つまり、callcc()を呼び出したときです。

このすべての結果は、仮想のPythonバリアントが'42'を出力するということです。

これがお役に立てば幸いです。そして、私の説明がかなり改善されると確信しています!

118
Keith Gaughan

コルーチンは、順番に作業を行ってから一時停止してグループ内の他のコルーチンを制御するいくつかの手順の1つです。

継続とは、あるプロシージャに渡される「関数へのポインタ」であり、そのプロシージャが完了したときに実行(「継続」)されるものです。

ジェネレーター(.NET)は、値を吐き出し、メソッドの実行を「一時停止」し、次の値を求められたときに同じポイントから続行できる言語構成体です。

32
zvolkov

Pythonの新しいバージョンでは、generator.send()を使用してジェネレーターに値を送信できます。これにより、pythonジェネレーターが効果的にコルーチンになります。

python Generatorとgreenletなどの他のジェネレータの主な違いは、Pythonでは_yield value_が呼び出し元に戻ることしかできないということです。greenletでは、target.switch(value)は、特定のターゲットコルーチンに移動し、targetが実行を継続する値を生成できます。

8
Yichuan Wang