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!もある)
  • クロージャを呼び出すと、元あった環境を呼び戻す。そのお陰で、レキシカルスコープが出来る。

クロージャの目的はレキシカルスコープの為にあるのだ。

環境作りがうまくいってれば、レキシカルスコープは簡単に作れる。