このページには、Scheme?のcall/ccについてのノウハウを集める予定。
尚、書き手の主観が反映されている為、必ずしも正しい内容が書かれているという保証は無し。
概要/まとめ
call/cc
dynamic-wind
- 副作用の無いコードを書いていれば、特にdynamic-windを使う必要は無い。
- バックトラック等が発生する時に、環境が何か特定の状態である必要のある時等に、その前準備/後始末を行う為に使われる(説明が分かりにくい。あとで直す事)。
- 他には?
goto/return的用法
(call/cc
(lambda (return)
...
...
...))
- call/ccで生成した継続を、call/ccの外に持ち出さないのであれば、特に厄介な要素を含まずに、gotoやreturnと同等の使い方が出来る。
- 上記のコードで、(return)を評価すると、続きのコードの評価を飛ばして、call/ccの外側に戻る。
- (return 'value)のように評価すれば、'valueが(call/cc ...)の返り値となる。
- 多値を返す事も出来る。その際はreceiveで受け取れば良い。
- (return)を実行せずに、call/ccの内側の最後まで到達した時は、最後に評価した値が(call/cc ...)の返り値となる。
結論:
- goto/return的用法において、コード中にcall/ccを置く事は、その位置にgotoのラベルを設定するのに近い。
- 但し、gotoとは違い、(returnのように)値を渡す事が出来る。
- goto/return的用法では、「call/ccまでの部分の評価」→「call/cc内の部分の評価」→「call/ccの後の部分の評価」という、評価順の流れ(タイムライン)自体は崩れない為、別に厄介な要素は特にない。
魔術的用法
call/ccで生成した継続を、call/ccの外に持ち出すような使い方をまとめて、ここでは「魔術的用法」と呼ぶ事にする。
「魔術的用法」で継続を用いる場合、コード全体が三つのパーツに分割される。
- call/ccに辿りつくまでの部分。
- call/ccによる継続は変数に保存される為、実際に変数に継続を保存する部分(つまり、call/cc)へと到達するコードが実際に実行されている必要がある。
- 要するに、(call/ccはgotoに似ているが、gotoとは違って)まだ一度も実行していないコードへといきなりジャンプする事はできない、という事。
- call/cc内部のproc。
- この部分は、call/ccに辿りつくまでの部分の延長上とみなす事が出来る。
- 魔術的用法を行う場合、この部分はそれほど重要ではない。
- call/cc内部のprocの処理が完了した以降の処理(つまり、call/cc以降)。
- call/ccで生成した継続を実行すると、「この位置」に戻ってくる。つまり、ここ以降が複数回実行される前提となり、参照透過性を保つ事が出来なくなる。
この性能を利用する事で、普通に実装する事が難しい、種々の「機能」を実装する事ができる。
- 但し、後述の「副作用」に関する制約も発生する事に注意。
バックトラック
- http://svn.tir.jp/viewcvs/Gauche-tir/trunk/lib/tir04/util/backtrack.scm?view=markup
(define-syntax backtrack/values
(syntax-rules ()
((_ . initial-values)
(let1 backtrack-point #f
(receive backtrack-values (let/cc cont
;; このset!はletrec用途なので副作用無し
(set! backtrack-point cont)
(values . initial-values))
(apply values backtrack-point backtrack-values))))))
(receive (backtrack n m) (backtrack/values 0 20)
;; ↑nには0が、mには20が渡される。
;; backtrackを実行すると、ここに戻ってくる。
;; そして、backtrackを実行した際の引数がnとmに新しく渡される。
;; (nとmの値が初期値かどうかで、初回実行かバックトラックかを判別できる)
;; 戻ってきた後でも、backtrackは変化しない。
;; (何回でもbacktrackを再利用できる)
(print n)
(when (< n m)
;; ↓で、receiveのところまで戻る。
(backtrack (+ 1 n) m))
(print "done."))
- backtrackを実行すると、その箇所にまで戻ってやり直す事が出来る。
- 上の例ではreceive内で全て完結しているが、もっと外側にまでbacktrackを渡していっても正常に動作する。
amb
バックトラックを、バックトラックと意識せずに書けるようにしたもの。多分。
基本的には、プログラムの実行される実行手順構造自体を総当たり検索し、条件にマッチする値を探すような挙動を行う。上手く説明しづらいが。
コルーチン
あとで書く。
部分継続
あとで書く。
副作用
あとで書く。
応用
あとで書く。
link
外部サイトへのリンク。
最終更新 : 2007/05/28 09:16:05 JST