(define (sqrt x)
(define (good-enough? guess)
(< (abs (- (square guess) x)) 0.001))
(define (improve guess)
(average guess (/ x guess)))
(define (sqrt-iter guess)
(if (good-enough? guess)
guess
(sqrt-iter (improve guess))))
(sqrt-iter 1.0))
このような内部定義が希望通りに振舞う理由を見るために, 環境モデルを使うことが出来る. 図3.11は式(sqrt 2)の評価中で, 内部手続き
good-enough?がguessを1にし一回目に呼び出されたところを示す.
環境の構造に注意しよう. sqrtは大域環境で, 手続きオブジェクトに束縛している記号である. 手続きオブジェクトに対応づけられている環境は大域環境である. sqrtが呼び出されると, 大域環境の下に新しい環境E1が作られ, そこでパラメタxは2に束縛される. 次にE1でsqrtの本体が評価される. sqrtの本体の最初の式は
(define (good-enough? guess) (< (abs (- (square guess) x)) 0.001))なので, この式を評価すると, 環境E1に手続きgood-enough?を定義する. より詳しくは, 記号good-enough?がE1の最初のフレームに追加され, E1を対応づけられた環境とする手続きオブジェクトに束縛される. 同様にimproveや sqrt-iterもE1の手続きとして定義される. 話を簡単にするため, 図3.11には good-enough?の手続きオブジェクトだけを示す.
局所手続きを定義した後, 式(sqrt-iter 1.0)がまだE1の中で評価される. そこでE1でsqrt-iterに束縛している手続きオブジェクトが, 引数を1として呼び出される. それにより, 環境E2が作り出され, sqrt-iterのパラメタguessが1に束縛される. sqrt-iterは次に(E2の) guessの値をgood-enough?の引数としてgood-enough?を呼び出す. また一つの環境E3が設定され, (good-enough?の引数)guessが1に束縛される. sqrt-iterとgood-enough?は両方ともguess という名前のパラメタを持っているが, それらは別々の局所変数で, 異ったフレームに存在する. また, sqrt-iterとgood-enough?手続きは, 両方とも環境部分にE1を持つので, E2とE3はE1を外側の環境とする. そのため, good-enough?の本体に現れる記号xは, E1に現れるxの束縛, つまり, 元のsqrt手続きが呼び出されたxの値を参照する.
環境モデルはこのように, 局所手続き定義をプログラムを部品化する有用な技法とする次の二つの重要な性質が説明出来る:
• 局所手続きの名前は, 大域環境で束縛されるのではなく, 手続きが走る時に作り出したフレームで束縛されるので, 囲んでいる手続きの外側の名前と衝突しない.
• 局所手続きは, パラメタの名前を自由変数として書くことで, 外側の手続きの引数にアクセス出来る. 局所手続きの本体は外側の手続きの評価環境の下の環境で評価されるからである.
問題 3.11
3.2.3節で局所状態を持つ手続きの振舞いを, 環境モデルでどう記述するかを見た. また内部定義がどう働くかを見た. 典型的なメッセージパッシング手続きはこの二つの面の両方を持つ. 3.1.1節の銀行口座手続きを考えよう:
(define (make-account balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(define (dispatch m)
(cond ((eq? m 'withdraw) withdraw)
((eq? m 'deposit) deposit)
(else (error "Unknown request -- MAKE-ACCOUNT"
m))))
dispatch)
一連の対話で生成される環境構造を示せ.
(define acc (make-account 50)) ((acc 'deposit) 40) 90 ((acc 'withdraw) 60) 30accの内部状態はどこに保持されているか. 口座をもう一つ定義したとする.
(define acc2 (make-account 100))二つの口座の局所状態はどのようにして別々に保たれるか. accとacc2で, 共有する環境構造はどの部分か.