Gauche:call/ccの使用例
call/cc の使用例
- 長いこと理解していなかった call/cc (call-with-current-continuation) ですが、
最近 R7RS や SRFI-34 の guard を調べていて、
だいぶ分かるようになったのでメモしておきます。
call/cc の使用例 1 (大域脱出)
(define (add x1 x2)
(call/cc
(lambda (k)
(unless (number? x1)
(k -10000))
(unless (number? x2)
(k -20000))
(+ x1 x2))))
(add 1 2) ; => 3
(add "a" 2) ; => -10000
(add 1 "b") ; => -20000
- 上記の例で、call/cc を評価すると、まず call/cc の継続手続きを k にセットして、
手続き (lambda (k) ...) が呼び出されます。
そして (k X) を実行すると、X が call/cc の戻り値となり、
ジャンプして call/cc の続きの部分から処理が継続されます。
- したがって、例えば (add "a" 2) を実行すると、
引数の "a" が数値ではないため (k -10000) が実行されて
call/cc の戻り値が -10000 になり、
それがそのまま add の戻り値になるため、結果が -10000 になります。
call/cc の使用例 2 (動的環境の巻き戻し)
(define-syntax guard
(syntax-rules ()
((guard (var clause ...) e1 e2 ...)
((call/cc
(lambda (guard-k)
(with-exception-handler
(lambda (condition)
((call/cc
(lambda (handler-k)
(guard-k
(lambda ()
(let ((var condition))
(guard-aux
(handler-k
(lambda ()
(raise-continuable condition)))
clause ...))))))))
(lambda ()
(call-with-values
(lambda () e1 e2 ...)
(lambda args
(guard-k
(lambda ()
(apply values args)))))))))))))
- 上記は、R7RS の 7.3 に載っている guard の一部です。
(これだけでは実行できませんが。。。)
- まず、((call/cc (lambda (guard-k) ...))) という部分ですが、
これは、内部から脱出して、動的環境を巻き戻すために存在しています。
その内側には (guard-k (lambda () X)) という記述があり、
これによって (lambda () X) が call/cc の戻り値になります。
そして、最初に戻って ((call/cc (lambda (guard-k) ...))) という部分をよく見ると、
call/cc がさらに一段括弧に囲まれているため、
この (lambda () X) が脱出後に呼び出されることになります。
- このようにすることで、内側の e1 e2 ... の部分に
dynamic-wind 等の動的環境の設定があった場合に、
確実に後始末をしてから次の処理を行う、という制御が行えます。
- 実際に上記 guard では、動的環境を抜けてから例外ハンドラを実行することで、
エラー処理を行いやすくしています(すでに後始末が済んでいるため)。
- また、上記 guard にはもう一箇所 ((call/cc (lambda (handler-k) ...)))
という部分があります。
これは、guard-k とは逆に、(handler-k (lambda () Y)) で
内側の動的環境に戻るために存在しています。
- これは、guard の例外ハンドラで例外を捕捉しなかった場合に、
元の動的環境に戻って、透過的に (すなわち、あたかも guard が存在しなかったかのように)
例外を上位に投げなおすために、このような処理となっています。
hamayama(2018/01/19 14:33:15 UTC)(2018/01/20 11:08:04 UTC)
(2018/01/21 03:03:13 UTC)(2018/01/22 02:08:39 UTC)
(2018/01/23 14:16:20 UTC)(2018/01/25 12:04:49 UTC)
(2018/02/06 12:10:14 UTC)(2018/05/02 15:27:16 UTC)
最終更新 : 2018/05/02 15:27:16 UTC