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


147

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


2
コルーチンと継続は実質的に同等かどうか疑問に思います。継続を使用してコルーチンをモデル化することは可能ですが、継続は厳密に強力であるため、コルーチンを使用して継続をモデル化することは可能ですか?
nalply

回答:


127

ジェネレーターから始めて、ジェネレーターが最も単純なケースであることを確認します。@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()ターンコールの場合とfoo()現在の継続と(cc、これでプログラムのポイントへの参照です)、callcc()と呼ばれていました。foo()現在の継続を呼び出す場合、それは基本的に、callcc()現在の継続を呼び出している値で戻るように指示することと同じであり、それを行うと、スタックが現在の継続が作成された場所、つまり、callcc()

これらすべての結果として、架空のPythonバリアントが出力されます'42'

それがお役に立てば幸いです。私の説明はかなり改善されると思います。


6
ワンNIT:区切りの継続が機能していますが、区切り文字のない継続はありません。okmij.org/ftp/continuations/undelimited.html#delim-vs-undelim
フランクShearar

2
それは良い点です。とは言っても、ほとんどの実用的なアプリケーションでは、人々が「継続」を言うとき、彼らは部分的/区切られた継続について話している。他のさまざまな種類の継続を導入すると、説明は多少混乱します。
キースガガン

1
継続は関数ではありませんが、関数に具体化できます。「とは言っても、ほとんどの実用的なアプリケーションでは、人々が「継続」と言うとき、彼らは部分的/区切られた継続について話している。」「継続」という用語のこのような使用法を指摘しますか?私はそのような使用法に会ったことがありません。また、call / ccを使用して、区切りなしの継続の例を示しました。区切られた継続の演算子は通常、「リセット」と「シフト」です(それらは他の名前を持つ場合があります)。
Ivancho 2014

3
私がこれを書いてから5年になるという事実から始めましょう。あなたはパーティーにいくらか遅れています。第二に、無制限の継続は関数ではないことを知っていますが、言語を単純に保ちながら、それらを参照せずにそれらがどのように機能するかを説明してみてください。平均的なプログラマーの観点から見ると、無制限の継続が返されないという事実は、それをワンショット関数にするだけであり、関数の定義に従って正しくないが、少なくとも理解できる
Keith Gaughan、2014

2
「コルーチンvsジェネレーター」を検索すると、これがGoogleで最初に表示される結果なので、パーティーに遅れることはありません。それらの違いについての良い情報を見つけたいと思っていました。とにかく私はそれを別の場所で見つけました。そして、私はあなたの継続についてのあなたの説明が間違っていると指摘する最初のものではありません。問題は、誰かがそれを間違え、おそらく別の何かに使用されている同じ単語に出会ったときに混乱する可能性があることです。
Ivancho 2014

33

コルーチンは、グループ内の他のコルーチンに制御を与えるために順番に仕事​​をしてから一時停止するいくつかの手順の1つです。

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

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


答えは正確ではないかもしれませんが、このレベルの質問では単純にしてみました。その上、私はこれらすべてを自分自身で本当に理解していません:)
zvolkov

PythonのジェネレーターはC#バージョンに似ていますが、指定した「関数」定義によって返される値を返すイテレーターオブジェクトのインスタンスを作成するための特別な構文として実装されます。
ベンソン

2
小さな修正:「...呼び出しスタックとすべての変数を含むが、それらの値は含まない」(または単に「すべての変数」を削除する)。継続は値を保存せず、単に呼び出しスタックを含みます。
nalply

いいえ、継続は「関数へのポインター」ではありません。最も単純な実装では、関数へのポインタが含まれ、環境はローカル変数を保持します。また、call / ccなどを使用して戻り値でキャプチャしない限り、戻りません。
NalaGinrut

9

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

pythonジェネレーターと他のジェネレーター(greenletなど)の主な違いは、pythonではyield value呼び出し側にしか戻れないことです。Greenletを使用している間target.switch(value)、特定のターゲットコルーチンに移動し、targetが引き続き実行される値を生成できます。


3
しかし、Pythonでは、すべてのyield呼び出しが「ジェネレータ」と呼ばれる同じ関数内になければなりません。あなたがすることはできませんyieldPythonの者が呼ばれている理由は、サブ機能、から半コルーチン Luaは持っていながら、非対称コルーチンを。(収量を
増やす

7
@ cdunn2001:(Winstonによるコメント)Python3.3は、サブジェネレーターから生成できる「yield from」式を導入しました。
Linus Caldwell
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.