継続理解再開
id:rsakamot:20090502:1241226261では,継続わからないとつぶやいた.
そしてそのまま放置してSICPへと移行した.
SICP4.3では,継続(call/ccは使わず,ひたすら関数を渡す)を用いて,amb評価器を実装した.
細かい動きは理解し切れていない気がするが,おおよその動きは理解できたと思う.
call/ccを理解するのは今だ!と思い,久しぶりにcall/ccへ取り組む.
前回わからなかった,break/next付きfor-eachはすんなり理解できた.これもSICPのおかげ?
特にcatchにはなかなか理解に苦しんだが,今回は理解できたと思う.
以下自分用に説明.
今回のcatch, throwは以下のようになっている.
(define *signals* '()) (define-syntax catch (syntax-rules (finally) [(_ (sig body ...) (finally follow ...)) (let* ((signals-backup *signals*) (val (call/cc (lambda (k) (set! *signals* (cons (cons 'sig k) *signals*)) body ...)))) (set! *signals* signals-backup) follow ... val)] [(_ (sig body ...)) (catch (sig body ...) (finally))])) (define-syntax throw (syntax-rules () [(_ sig val) ((cdr (assq 'sig *signals*)) val )]))
すごーく簡単に説明すると,
- catchで,sigと継続kの対を*signals*に登録する.*signals*は連想リストになっている.
- throwを実行するときに,*signals*の連想リストから,'sigに該当する対を探し,そのcdrを実行することで,登録されている継続を呼び出す
となる.
今回のサンプルプログラムは以下のようになっている.
(define (div n d) (if (= d 0) (throw DivideZeroError (print "Divide by Zero")) (/ n d))) (define (percentage a b) (catch (DivideZeroError (print (* (div a b) 100.0) "%")) (finally (print "follow..."))))
実行すると以下のようになる
;; throwを呼び出さない gosh> (percentage 1 10) 10.0% follow... #<undef> ;; throwを呼び出す gosh> (percentage 1 0) Divide by Zero follow... #<undef>
このままでは理解しにくいので,percetageを展開し,さらにちょっと追加する.
(define (percentage a b) (let* ((signals-backup *signals*) (val (call/cc (lambda (k) (set! *signals* (cons (cons 'DivideZeroError k) *signals*)) (print (* (div a b) 100.0) "%") 'percent-hoge)))) ;追加 (set! *signals* signals-backup) (print "follow ...") val))
このpercentageで上のサンプルプログラムを実行すると,
gosh> (percentage 1 10) 10.0% follow ... percent-hoge gosh> (percentage 1 0) Divide by Zero follow ... #<undef>
のようになる.
throwが呼ばれない場合は,通常通りにvalがletによって初期化され,percent-hogeとなる.
throwが呼ばれる場合は,valが,throwの結果#
つまり,valの初期化が継続地点となっている.