これは、継続通過型である。
JavascriptがLispのですが、C.
の服を身に着けている構文としてJavascriptがその中核に関数型言語なので、本当にクレイジーなトリックが継続渡しスタイルのように、可能です。しかし、これらのトリックは頭痛を誘発します。
要約すると、継続は、次に何をすべきかという概念です。関数とまったく同じように、呼び出し可能なものとして利用できます。私は時には継続を呼び出しフレームのスタックとして見ることもできます。関数呼び出しのスタックを実行状態として保存し、後でこの状態に戻すか、単に呼び出すことができます。
誰かが、継続的な通過スタイルにコードを変換することによって、継続の力を得ることができることを実証しました。うわー!それは本当に印象的です:
あなたは継続性の力を持っています。
ここで、Javascriptの問題はCの構文です。 Cの構文でソースコードを変換するのは難しいです。 Lisp構文では簡単だが、やはり面倒でエラーが起こりやすい。
本当に独創的な人たちが私たちのために努力してくれたことは幸いです。このハードワークは、Javascriptパーサーの使用を必要とします。なぜなら、この変換は実際には何を意味するのでしょうか?要約すると、最初に実際に行われたことが最初に来るように操作の順序を並べ替えることを意味します。
f(g(a + x))
a + x
添加は次いで、関数呼び出しg()
f()
、最初に行われます。サブ式は3つあります。 CPS変換では、サブ式の結果が継続に渡されます。これには、一時的な継続として多くの内部ヘルパー関数の作成が含まれます。これは、以下に示すように、複雑で面倒なことがあります。
http://en.wikipedia.org/wiki/Continuation-passing_style例関数
(define (pyth x y)
(sqrt (+ (* x x) (* y y))))
が
(define (pyth& x y k)
(*& x x (lambda (x2)
(*& y y (lambda (y2)
(+& x2 y2 (lambda (x2py2)
(sqrt& x2py2 k))))))))
に変換され、これはJavacript
function pyth(x, y) {
return Math.sqrt(x * x + y * y);
}
に対応するが、*、+とMath.sqrt()関数ではありませんCPSが理にかなっています。
しかし、*、+、Math.sqrt()がWebサービスであると仮定します。これは、Javascript Webサービスの呼び出しがの非同期であるため、重要です。非同期呼び出しを使用して作業しているすべての人は、それらの結果を結合するのがどれだけ複雑かを知っています。前処理ライブラリまたは複数のジェネレータを使用すると、非同期の結果に簡単に対処できます。
それでは、別の方法での例を書いてみましょう:
function pyth(x, y) {
return sqrt(add(mul(x, x), mul(y, y)));
}
その後、CPS変換を次のようになります。
function pyth_cps(x, y, k) {
mul_cps(x, x, function(x2) {
mul_cps(y, y, function(y2) {
add_cps(x2, y2, function(x2py2) {
sqrt_cps(x2py2, k);
})
})
});
}
我々は結果のコードはインサイドアウト引き裂かと積極的に行われていることがわかり読めない。各関数が変換されます。彼らはすべて魔法のパラメータkを得る。それが継続です。 javascriptでは、操作の結果を取得する関数です。コールスタックkのどこかに深いところが呼び出されます。この例では、ここには示されていないsqrt()のCPS変換です。
CPSで変換された関数は返されません。彼らは計算の結果を続けるだけです。これは、スタックが枯渇する可能性があります。すべてのJavascript CPSトランスフォーマーがこれを処理する必要があります。スキームでは、すべての呼び出しが末尾呼び出しであるため、これは必要ではありません。テールコールは追加の呼び出しフレームを必要としません。 Javascriptでは、トランポリンまたは同様の技術が必要です。継続を直接呼び出すのではなく、ヘルパーを呼び出して結果と継続を渡します。ヘルパーは無限ループで実行され、スタックの枯渇を常に呼び出して返します。
このCPSはなぜ私たちに継続の力を与えるのですか?それは、継続は単に次のことであるからです。私たちが常に次のことを追加のパラメータkとして実行し、常に現在の式の結果を渡すなら、この概念をコードで実現しました。しかし、これまで見てきたように、これはいつも持ち歩くのが面倒です。
ソースコードのプリプロセッサに手間をかけても、手数料がかかります。なぜ継続を使うべきですか?制御フローを抽象化することは可能です。 WebアプリケーションフレームワークであるSeasideは、ブラウザのステートレスな要求フローを抽象化するために継続を使用します。ユーザーのやりとりを簡潔にモデル化することができます。ユーザーの対話は、もはや要求ではなく対話の流れで考える必要はありません。これは、継続の力の多くの例の一つにすぎません。この力はまた、多くの人々には奇妙で幾分恐ろしいようです。
私はCPSをよく知っていますが、一般的な形式の命令コードをa-la-javascriptに変換するのは簡単ではありません。どのようにシーケンシャルコードからCPSに自動的に変換しますか? –
最初にソースコードを解析し、最初の部分式が評価され、式の結果が継続に渡されるように式を並べ替えます。 I. http://en.wikipedia.org/wiki/Continuation-passing_styleで説明されているのと同じ操作を行いますが、Javascriptで行います。 – nalply