SICP 4.1.2 Representing Expressins その2
俺様処理系にはデバッグ機能が無い事に気がついた。
それはさておき、僕なりに考えたデバッグ方法。traceを使うと、引数が省略されて困るときがある。特に素人はかなり困る。表示もそうだけど、同じ引数でその手続きだけ実行したいときがある。
CALL cond-clauses (cond ((...) ...) (x 10) (else -3))
トップレベルで、(define bp0 '())みたいに変数を定義しておき、ソースの該当箇所で(set! bp0 exp)のように値を設定しておくと、その関数を抜けた後もそのときの値が見れる。これで、ローカルに作った手続きのデバッグもできそう。頑張れば、bp1→bp2って順繰りに自動で設定することもできそうだ。もっと良い方法は絶対にあるんだけど、当面はこれで十分だ。bpはBreakPointの略。昔作ったROMデバッガの仕組みを思い出したから。
今回はcondからifへの変換の話。俺様処理系には、>とかeq?とかしゃれた演算子はないので、とりあえずこんな感じの手続きを考える。
(cond ((null? x) 5) (x 10) (else -3))
cond-ifによって、cond文が、if文に変換される。ここは俺様処理系とは関係ないので、写経で動くはず。
(cond->if '(cond ((null? x) 5) (x 10) (else -3)))
とすると、こんな式が返る。
(if (null? x)
5
(if x
10
-3))
良い感じ。
Exercise 4.3 飛ばす
Exercise 4.4 その1 and と orをスペシャルフォームとして実装しなさい。
まずは、普通にeval-oに手続きを追加したバージョン。andとorだと、gaucheのand、orと混乱するので、and-o、or-oにする。
(define (eval-o exp env)
(cond ((self-evaluating? exp) exp)
((variable? exp) (lookup-variable-value exp env))
((quoted? exp) (text-of-quotation exp))
((call? exp) (apply-o (eval-o (call-operator exp) env)
(list-of-values (call-operand exp) env)))
((assignment? exp) (eval-assignment exp env))
((definition? exp) (eval-definition exp env))
((if? exp) (eval-if exp env))
((and-o? exp) (eval-and (cond-tests exp) env)) ;追加
((or-o? exp) (eval-or (cond-tests exp) env)) ;追加
((and-d? exp) (eval-o (and->if exp) env)) ;追加
((or-d? exp) (eval-o (or->if exp) env)) ;追加
((lambda? exp)
(make-procedure (lambda-parameters exp)
(lambda-body exp)
env))
((begin? exp)
(eval-sequence (begin-actions exp) env))
((cond? exp) (eval-o (cond->if exp) env))
((application? exp)
(apply-o (eval-o (operator exp) env)
(list-of-values (operands exp) env)))
(else
(error "Unknown expression type nyo -- EVAL" exp))))
;;;and-oとor-oの実装
;;;and-o
(define (and-o? exp) (tagged-list? exp 'and-o))
(define (eval-and exp env)
(if (null? exp)
true
(if (eval-o (first-operand exp) env)
(eval-and (rest-operands exp) env)
false)))
(define (cond-tests exp) (cdr exp))
;;;or-o
(define (or-o? exp) (tagged-list? exp 'or-o))
(define (eval-or exp env)
(if (null? exp)
false
(if (eval-o (first-operand exp) env)
true
(eval-or (rest-operands exp) env))))
次が、if文への変換。derivedだから、and-d、or-dで実装。
;;;and-d
(define (and-d? exp) (tagged-list? exp 'and-d))
(define (and->if exp) (expand-and (cdr exp)))
(define (expand-and exp)
(if (null? exp)
'true
(let ((first (car exp)) (rest (cdr exp)))
(make-if first
(expand-and rest)
'false))))
;;;or-d
(define (or-d? exp) (tagged-list? exp 'or-d))
(define (or->if exp) (expand-or (cdr exp)))
(define (expand-or exp)
(if (null? exp)
'false
(let ((first (car exp)) (rest (cdr exp)))
(make-if first
'true
(expand-or rest)))))
and-ifの結果
(and->if '(and-d (cond1 'aaa) (cond2 'bbb) (cond3 'ccc)))
gosh> (if (cond1 'aaa) (if (cond2 'bbb) (if (cond3 'ccc) true false) false) false)
'falseの変わりにfalseや#fと書くと、and->ifの戻りに#fが入ってしまい俺様evalで評価できずエラーになった。evalや、true/falseの作りでどうにでもなるけどね。
動くソースはここ
と書いてから、Gaucheクックブックにデバッグのヒントを発見。
The comments to this entry are closed.


Comments