2005/05/29

引き続きcall/ccに悩み中。Dibvig本の「3.5 内部定義」には、本文で定義したcalcというプロシージャをcall/ccを削除して定義し直せ、という練習問題がある。call/ccを使ったバージョンは以下のとおり。ちなみに、これは本文のオリジナルバージョンではなく、問題3.5.2の前半までに対する僕の回答。

; Dibvig-3.5.2-1
(define calc1
(lambda (exp)
(define-syntax complain
(syntax-rules ()
((_ e1 e2 e3) (e1 (list e2 e3)))))
(call/cc
(lambda (ek)
(define apply-op
(lambda (ek op args)
(op (do-calc ek (car args)) (do-calc ek (cadr args)))))
(define do-calc
(lambda (ek exp)
(cond
((number? exp) exp)
((and (list? exp) (= (length exp) 3))
(let ((op (car exp)) (args (cdr exp)))
(case op
((add) (apply-op ek + args))
((sub) (apply-op ek - args))
((mul) (apply-op ek * args))
((div) (apply-op ek / args))
(else (complain ek "invalid operator" op)))))
(else (complain ek "invalid expression" exp)))))
(do-calc ek exp)))))

gosh> (calc1 '(div 2 3))
=> 0.6666666666666666

gosh> (calc1 '(div (div (div 3 2)) 2))
=> ("invalid expression" (div (div 3 2)))

エラー処理もうまくいっている。で、ここからcall/ccを削除したいんだけど、単純に次のようにしてはダメらしい。

; Dibvig-3.5.2-2
(define calc2
(lambda (exp)
(define-syntax complain
(syntax-rules ()
((_ e1 e2) (list e1 e2))))
(let ()
(define apply-op
(lambda (op args)
(op (do-calc (car args)) (do-calc (cadr args)))))
(define do-calc
(lambda (exp)
(cond
((number? exp) exp)
((and (list? exp) (= (length exp) 3))
(let ((op (car exp)) (args (cdr exp)))
(case op
((add) (apply-op + args))
((sub) (apply-op - args))
((mul) (apply-op * args))
((div) (apply-op / args))
(else (complain "invalid operator" op)))))
(else (complain "invalid expression" exp)))))
(do-calc exp))))

gosh> (calc2 '(div 2 3))
=> 0.6666666666666666

gosh> (calc2 '(div (div (div 3 2)) 2))
=> *** ERROR: operation / is not defined between ("invalid expression" (div (div 3 2))) and 2
Stack Trace:
_______________________________________

エラー処理がうまくない。一回だけエラー処理をしているけど、そこで抜けられていない。apply-opの再帰を継続引き渡しで書き直せばいいのかな?

No comments :