トレース機能を追加しました。
実装してから、出題の意図としては俺言語の方で追加するんじゃないかとも思いましたが、素のSchemeの方で追加できるようにしました。
テストは100点を目指すよりは合格すればOKのタイプ。
まずは、トレースON/OFFを制御する信号と、トレース対象の関数名(シンボル)を保存するためのListを宣言
(define *trace-en* #f)
(define *trace-list* '())
APIっぽいの
(define trace-on
(lambda ()
(set! *trace-en* #t)))
(define trace-off
(lambda ()
(set! *trace-en* #f)))
(define trace
(lambda (s)
(set! *trace-list* (cons s *trace-list*))))
引数で与えられたシンボルが、トレース対象かどうかの判定関数
(define trace?
(lambda (s)
(and *trace-en*
(member s *trace-list*))))
evaluateの中身(invoke周辺)
(let ([func (car e)]
[args (evlis (cdr e) env)])
(when (trace? func) (format #t "call ~A ~A~%" func args))
(let ([result (invoke (evaluate func env) args)])
(when (trace? func) (format #t "rerurn ~A ~A result ~A ~%" func args result))
result)))]
引数が複数回評価されないように、先頭でfunc、argsに代入し、funcがトレース対象なら、呼び出し前と、呼出し後で関数名と引数、戻り値を表示していま
す。
プログラムはインタープリターに打ち込んでられないので、リストを評価するための関数も追加。長門プロンプトで、自分の処理系が出しているメッセージであることを強調しています。
(define chap1-scheme-bat
(lambda (args)
(dolist (s args)
(format #t "YUKI.N> ~A~%" (evaluate s env.global)))))
あとはこんな感じで階乗を実行してみます。
(load "./chap1.scm")
(trace-on)
(trace 'fact)
(chap1-scheme-bat
'((set! fact
(lambda (x)
(if (= x 1)
1
(* (fact (- x 1)) x))))
(fact 5)))
これを実行すると、
call fact (5)
call fact (4)
call fact (3)
call fact (2)
call fact (1)
rerurn fact (1) result 1
rerurn fact (2) result 2
rerurn fact (3) result 6
rerurn fact (4) result 24
rerurn fact (5) result 120
YUKI.N> 120
このように表示されます。