SICPを読む(99) 問題 3.10 - 局所変数の入れ物とフレーム
うぅん。SICPの説明がなんか納得いかない。
問題 3.10
make-withdrawのletをlambdaに書き直して、環境モデルを書き直す問題。
(define (make-withdraw internal-amount) ((lambda (balance) (lambda (amount) (if (>= balance amount) (begin (set! balance amount) amount) (error "Insufficient funds.")))) internal-amount))
lambdaの引数が環境を生み出すんだから、make-withdrawを呼び出した時の環境はこうなってて、
(define W1 (make-withdraw 100)) (define W1 (lambda (internal-amount) .. (define W1 (lambda (balance) .. ; +---------------------------+ ; | balance : 100 | ; | +----------------------+ | ; | | internal-amount:100 | | ; | | +大域環境-------+ | | ; | | | make-withdraw | | | ; | | +---------------+ | | ; | +----------------------+ | ; +---------------------------+
lambdaが大域環境を包み込んでる感じなんだよ。
lambdaだから無名な訳で、フレームには名前が無い。
そのままじゃ忘れちゃうので、名前を付けておこう。
(define W1 (lambda (amount) ... ; +大域環境----------------------------------------- ; | make-withdraw ; | +W1-(lambda (amount) ... + ; | | balance : 100 | ; | | internal-amount : 100 | ; | +-------------------------+ ; +-------------------------------------------------
このとき、環境も一緒に保存しておくのが、Scheme流。
W1を呼び出す時に、前の環境を戻す。
(W1 50) ; +------------------------------- ; | amount : 50 ; | +---------------------------+ ; | | balance : 50 | ; | | +----------------------+ | ; | | | internal-amount: 100 | | ; | | | +大域環境-------+ | | ; | | | | make-withdraw | | | ; | | | | W1 | | | ; | | | +---------------+ | | ; | | +----------------------+ | ; | +---------------------------+ ; +--------------------------------
これがクロージャ。
こいつを理解するのも大変だった気がする・・・。
イメージの問題だけど、大域環境の中で作業して、外へのポインタと考える手もある。そっちのほうが自然かも。
重要なことは
- lambdaはローカルな環境を作り出す。
- トップレベルのdefineは大域環境にローカル環境を保存できる唯一の手段。(set!もある)
- クロージャを呼び出すと、元あった環境を呼び戻す。そのお陰で、レキシカルスコープが出来る。
クロージャの目的はレキシカルスコープの為にあるのだ。
環境作りがうまくいってれば、レキシカルスコープは簡単に作れる。