curious course on coroutines and concurrency 2
python前の投稿で、yieldによって値を受け渡したり、ジェネレーターの呼び出し元とジェネレーターの中を行き来したりすることができるようになったのを見た。
ただ、ここには一つ制約があって、ジェネレーターが別のジェネレーターを呼び出すときに問題が生じる・・らしい (todo: が、その問題が何なのか、まだよく分かってないので、分かったら書き直す)。 これを回避するために、トランポリンというテクニックを使う。
def foo(val):
yield f"foo: {val}"
def bar():
ret = yield foo("bar")
print(ret)
yield
def baz():
b = bar()
f = b.send(None)
ret = f.send(None)
b.send(ret)
baz()
これは、次のように動作する:
baz()呼び出しによって、bazの中身がスタート。ジェネレーターbが生成されるb.send(None)によって、barの中身がスタート。yield foo("bar")によって、ジェネレーターが生成される。これがyieldによってbazに返され、fに代入される。また、制御がbazの方に戻るf.send(None)によって、fooの中身がスタート。yield f"foo: {val}"によって、値"foo: bar"が生成される。これがyieldによってbazに返され、retに代入されるb.send(ret)によって、barの中身に制御が戻り、(barの中の)retに値が代入される。これがprint(ret)によって表示され、次のyieldでbazに制御が戻る
ポイントは何かというと・・ジェネレーターと呼び出し元との行き来は、全て、一番根っこの呼び出し元とジェネレーターとの間で行われる、ということ(つまり、上の例で言うと、fooとbaz、barとbazの間でyieldによる行き来は起こるが、ジェネレーターであるbarとbazの間では起こらないということ)。
なので、根っこの呼び出し元のbazが、全てのジェネレーターを保持し、全ての返り値を受け取り、それを適切なジェネレーターにsendするという役割を一手に引き受けている。