Ex. 4.52
(if-fail (a ...) (b ...))
という式の継続処理を考える。 適当な s0, f0 を if-fail の引数として
a → s0 (a が成功継続を選んだとき) a ⇒ b → s0 (a が失敗継続を選んだとき)
と動作すればよいと思われる。つまり、単に a を実行すればよい。実行時の aの成功継続に s0 を渡し、a の失敗継続に (b→ s0)を渡すだけ。
(define (if-fail? exp) ; 追加 (tagged-list? exp 'if-fail)) (define (if-fail-consequent exp) (cadr exp)) ; 追加 (define (if-fail-alternative exp) (caddr exp)) ; 追加 (define (analyze-if-fail exp) ; 追加 (let ((cprocs (analyze (if-fail-consequent exp))) ; TRUE節 (aprocs (analyze (if-fail-alternative exp)))) ; ELSE節 (lambda (env succeed fail) (cprocs env ; if-fail? の TRUE節の式を実行する succeed ; 成功継続は通常どおり (lambda () ; 失敗継続 (aprocs env succeed fail)))))) ; 失敗継続の中身は(ELSE節の式 → succeed)合成関数 (define (analyze exp) (cond ((self-evaluating? exp) ... ((if-fail? exp) (analyze-if-fail exp)) ; 追加 ... (define primitive-procedures (list (list 'car car) ... (list 'even? even?) ; 追加 ...
これで、p260の例は正しいのだが
;;; Amb-Eval input: (if-fail (let ((x (an-element-of '(1 3 5 )))) (require (even? x)) x) 'all-odd) ;;; Starting a new problem ;;; Amb-Eval value: all-odd
おかしいのは、リストに偶数が入っているときである。
;;; Amb-Eval input: (if-fail (let ((x (an-element-of '(1 3 4 5 )))) (require (even? x)) x) 'all-odd) ;;; Starting a new problem ;;; Amb-Eval value: 4 ← ここで偶数が見つかっているのに ;;; Amb-Eval input: try-again ;;; Amb-Eval value: all-odd ← 最後に「全部奇数」というメッセージがでるのはおかしい。
このように、リストに偶数があろうがなかろうが、最後に all-odd の表示を実行してしまうのである。
これを修正しようとして、望ましい動作を考えてみると、
(例1)...(an-element-of '(1 3 5))...のときの継続処理 1 → require ⇒ 3 → reauire ⇒ 5 → require ⇒ all-odd
であり、一方
(例2)...(an-element-of '(1 3 4 5))...のときの継続処理 1 → require ⇒ 3 → reauire ⇒ 4 → x → driver-loop ⇒ 5 → require ⇒ 終了(all-oddを表示せずに終了)
という継続処理が必要である。
この継続列の最後の部分に注目すると
(例1) require ⇒ all-odd (例2) require ⇒ 終了(all-oddを表示せずに終了)
となっている。(例1)と(例2)の require の失敗継続が異なっている。
これを実現するには、(例2)で4が見つかった時点で、成功継続の引数に渡す失敗継続から、'all-odd を消し去らねばならない。
これはなかなか難しい。なぜなら現状、if-failのTRUE節は一つの式であり、ELSE節側からこの式が一度でも成功するか否かを判定する方法がないからである。(ELSE節側とTRUE節間で通信する手段がない)。
現状では、ELSE節側(all-odd側)は自分を失敗継続として、TRUE節(成功継続側)に渡すだけの仕組みしかないのである。成功継続がわも set! 式以外は、失敗継続をそのまま使うだけの実装になっている。
これを改善するためには、ELSE節側つまり失敗継続側で、TRUE節つまり成功継続側の成功、失敗をモニターできるような機構を追加しなければならないと思われる。
たぶん評価器全体をかなり変更しないと実装できない。たとえば、すべての式で、成功、失敗どちらの継続を選択したかの履歴を失敗継続に埋め込んでいって、if-elseの失敗継続は自分に関係のある継続の履歴を見ることができるようにするとか。これはかなり面倒そうである。
もっとうまい手があるか?エラーメッセージを "no-more-even" にするとか。根本は何も解決してないけれど。