誰かがそれがどのように機能するかについての良いガイドを持っていますか?視覚的な補助機能を備えたものがあればいいのですが、私が出会ったすべてのガイドは、私がそれを新たに取り入れることが必要なのと同じことを言っているようです。
5 に答える
これは、CS ラボのホワイトボードに残されていた図です。りんごをいくつか取ってきて、開始する前に続きをつかみます。あなたは森の中をさまよってリンゴを集め、最後にリンゴに継続を適用します。突然、あなたは自分が森に入る前の場所にいることに気がつきました。
(display
(call/cc (lambda (k)
(begin
(call-with-forest
(lambda (f)
(k (collect-apples f))))
(get-eaten-by-a-bear)))))
=> some apples (and you're not eaten by a bear)
Bar Mitzvah と埋められた金が関与した可能性があると思います。
PLAIの継続部分を見てください。これは非常に「実用的」であり、継続を理解するのに役立つ「ブラックホール」の視覚化を使用しています。
コードに反映できないので、call/cc の視覚的表現は決して好きではありません (はい、想像力が乏しいです) ;)
とにかく、他の言語の例外に慣れている場合は、call/cc ではなく call/ec (エスケープ継続) から始める方が簡単だと思います。
値に評価する必要があるコードを次に示します。
(lambda (x) (/ 1 x))
xが「0」に等しい場合はどうなりますか? 他の言語では例外をスローできますが、スキームはどうでしょうか? 私たちも投げられます!
(lambda (x) (call/ec (cont)
(if (= x 0) (cont "Oh noes!") (/ 1 x))))
call/ec (および call/cc) は、ここでは "try" のように機能します。命令型言語では、値を返すか例外をスローするだけで、関数から簡単に飛び出すことができます。ファンクショナルでは飛び出してはいけない、何かを評価するべきです。そして call/* が助けに来ます。「call/ec」の下の式を、1つの引数を持つ関数(私の場合は「cont」という名前)として表します。この関数が呼び出されると、call/* 全体がその引数に置き換えられます。
したがって、文字列に置き換える(cont "Oh noes!")
場合 。(call/ec (cont) (if (= x 0) (cont "Oh noes!") (/ 1 x)))
"Oh noes!"
call/cc と call/ec は、ec の方が実装が簡単であることを除いて、ほぼ同じです。ジャンプアップのみ可能で、CCは外側からジャンプダウンできます。
コール スタックを視覚化すると役立つことがわかりました。式を評価するときは、すべてのステップでコール スタックを追跡します。(たとえば、http://4.flowsnake.org/archives/602を参照してください) ほとんどの言語ではコール スタックが暗黙的であるため、これは最初は直感的ではないかもしれません。直接操作することはできません。
ここで、継続をコール スタックを保存する関数と考えてください。その関数が (値 X で) 呼び出されると、保存されたコール スタックが復元され、X が渡されます。
call/cc の習得に近道はありません。The Scheme Programming LanguageまたはTeach Yourself Scheme in Fixnum Daysの章を読んでください。