私はOOPの機能していないバックグラウンドから来ているので、continuation passingに関するいくつかのオンラインの例を完全に視覚化することに問題があります。また、Schemeのような関数型言語は、引数の型や戻り値の型を指定する必要がないので、私が正しく考えているかどうかは不明です。継続渡しスタイルの中間値と戻り値
C#が、私は、パターンを適用する方法を確認するために、Wikipediaの記事からの最初の例を取って、強い型付けでのC#に移植することを試みたが、ラムダをサポートしているため:
// (Scheme)
// direct function
(define (pyth x y)
(sqrt (+ (* x x) (* y y))))
// rewriten with CPS
(define (pyth& x y k)
(*& x x (lambda (x2)
(*& y y (lambda (y2)
(+& x2 y2 (lambda (x2py2)
(sqrt& x2py2 k))))))))
// where *&, +& and sqrt& are defined to
// calculate *, + and sqrt respectively and pass the result to k
(define (*& x y k)
(k (* x y)))
をので、書き換えC#でのCPS pyth&
バージョンがもたらした:
// (C#6)
// continuation function signature
delegate double Cont(double a);
// *&, +& and sqrt& functions
static double MulCont(double a, double b, Cont k) => k(a * b);
static double AddCont(double a, double b, Cont k) => k(a + b);
static double SqrtCont(double a, Cont k) => k(Math.Sqrt(a));
// sqrt(x*x + y*y), cps style
static double PythCont(double x, double y, Cont k) =>
MulCont(x, x, x2 =>
MulCont(y, y, y2 =>
AddCont(x2, y2, x2py2 =>
SqrtCont(x2py2, k))));
私が代わりにdouble
のジェネリック医薬品を使用することもできますが、署名はbのでしょうe長く。とにかく、私はわからないことです:
が正しい(すなわち
Func<double, double>
)上記Cont
署名ですか? fnが続くべきか?パラメータを受け入れて処理し、同じ型の値を返しますか?私が最初にコンティニューについて読み始めたとき、このコンティニュアス機能がコールスタックの各ステップで呼び出されると感じましたが、上の例では
sqrt&
にしか渡されず、他のコールはすべてラムダを取得します。実際に中間の値を元の継続に「渡す」必要はありません。上記の関数の上のコードは、基本的にはk(Math.Sqrt(x * x + y * y))
に類似しているので、中間の「フック」についての私の前提が間違っていますか?
私にはうまく見えます。唯一の問題は、テールコールを使用することです。この例では問題ありませんが、あなたのスタックはもう少し多くのコードで非常に高速になります。 – leppie
フックについてのあなたの直感が分からずにこの質問に答えるのは難しいです。 CPSを使用すると、呼び出し先は継続を複数回呼び出すことも、まったく呼び出すこともなく、後で呼び出しられるようにどこかに保存することもできます。この例はかなりシンプルなので、各継続は次の段階を呼び出すだけですが、一般的にそうする必要はありません。 – Lee
@Lee:ありがとう、おそらくこれらの例は単純化されているので、いくつかの重要な点は失われますが、継続がどのように制御状態*(https://en.wikipedia。 org/wiki/Continuation)、私にとっては単純な古いコールバック関数のように見えます。 – Lou