(list (list 'initialize-stack
(lambda () (stack 'initialize)))
(list 'print-stack-statistics
(lambda () (stack 'print-statistics))))
のように初期化して, 基本計算機モデルにスタック統計量を印字する演算を追加する. これはmake-stackの新版である:
(define (make-stack)
(let ((s '())
(number-pushes 0)
(max-depth 0)
(current-depth 0))
(define (push x)
(set! s (cons x s))
(set! number-pushes (+ 1 number-pushes))
(set! current-depth (+ 1 current-depth))
(set! max-depth (max current-depth max-depth)))
(define (pop)
(if (null? s)
(error "Empty stack -- POP")
(let ((top (car s)))
(set! s (cdr s))
(set! current-depth (- current-depth 1))
top)))
(define (initialize)
(set! s '())
(set! number-pushes 0)
(set! max-depth 0)
(set! current-depth 0)
'done)
(define (print-statistics)
(newline)
(display (list 'total-pushes '= number-pushes
'maximum-depth '= max-depth)))
(define (dispatch message)
(cond ((eq? message 'push) push)
((eq? message 'pop) (pop))
((eq? message 'initialize) (initialize))
((eq? message 'print-statistics)
(print-statistics))
(else
(error "Unknown request -- STACK" message))))
dispatch))
問題5.15から5.19は, レジスタ計算機シミュレータに追加する他の有用な監視と虫とり機能を示す.
問題 5.14
図5.11に示す階乗計算機を使い, 小さいnのいくつかに対しn!を計算するのに必要な退避の回数とスタックの最大深さを計測せよ. そのデータからn>1でn!を計算するのに使う退避演算の全数と, スタックの最大深さのnに関する式を決定せよ. このそれぞれはnの線形関数で, 二つの定数で決ることに注意しよう. 統計量を印字するのに, 階乗計算機にスタックを初期化し, 統計量を印字する命令を追加しなければならない. また(図5.4のGCD計算機でしたように)毎回get-register-contents,
set-register-contents!, とstartを起動しないでも, 繰り返しnの値を読み込み, 階乗を計算し, 結果を印字するように計算機を修正したくなるであろう.
問題 5.15
レジスタ計算機シミュレーションに, 命令計数(instruction counting)
を追加せよ. つまり計算機モデルに実行した命令の回数を覚えさせる. 計算機モデルのインターフェースを拡張し, 命令回数の値を印字し, 回数を零に設定するメッセージを受け入れるようにせよ.
問題 5.16
シミュレータに命令トレース(instruction tracing)が出来るよう拡張せよ. つまり各命令を実行する前に, シミュレータは命令の文字列を印字する.
トレースを開始と停止するtrace-onとtrace-offメッセージを計算機モデルが受け入れるようにせよ.
問題 5.17
問題5.16の命令トレースを拡張し, シミュレータが命令を印字する前に制御器の命令の直前にあるラベルを印字するようにせよ. 命令の計数(問題5.15)に干渉せずに行うよう注意せよ. シミュレータが必要なラベル情報を保存するにしなければならない.
問題 5.18
5.2.1節のmake-register手続きを修正し, レジスタがトレース出来るようにせよ. レジスタはトレースを開始と停止するメッセージを受け入れる. レジスタをトレースする時は, レジスタへの値の代入では, レジスタ名, レジスタの古い内容と代入する新しい内容を印字する. 計算機モデルのインターフェースを拡張し, 指示した計算機レジスタのトレースの開始と停止が出来るようにせよ.
問題 5.19
Alyssa P. Hackerは自分の計算機設計の虫とりに使えるよう, シミュレータにブレークポイント(breakpoint)機能が欲しかった. 彼女のためにこの機能を組み込むべく雇われたとしよう. 彼女は, シミュレータが停止し,
彼女に計算機の状態が調べられるよう, 制御器の命令列の場所が指定出来ることを欲している. 今与えられたラベルの後n番目の命令の直前にブレークポイントを設定する手続き
(set-breakpoint 〈machine〉 〈label〉 〈n〉)を実装しようとしている. 例えば
(set-breakpoint gcd-machine 'test-b 4)はgcd-machineで, レジスタaへの代入の直前にブレークポイントを組み込む. シミュレータがブレークポイントに達すると, ラベルとブレークポイントの距離を印字し, 命令の実行を停止する. そこでAlyssaはシミュレートする計算機の状態を操作するget-register-contentsとset-register-contents!を使うことが出来る. その後彼女は
(proceed-machine 〈machine〉)といって実行が続行出来なければならない. 彼女はまた指定したブレークポイントを
(cancel-breakpoint 〈machine〉 〈label〉 〈n〉)により削除出来, また
(cancel-all-breakpoints 〈machine〉)