プログラミング再入門

プログラミングをもう一度ちゃんと勉強する読書ノート

SICP 3.2.3 Frames as the Repository of Local State

局所状態の保存場所としてのフレーム。

ノート

手続きとはlambdaの本体とlambdaが評価されたときのフレームへの参照をもったペア。
その手続きが呼び出された時には引数の値に拘束した仮引数を保存したフレームを作り、その外部環境としてその手続きが参照しているフレームを参照し、その環境下でlambdaの中身を評価する。

最初に(define make-withdraw ...)で隠されたlambdaが評価される。balanceを引数に取る本体と、lambdaが評価された環境すなわちglobalへの参照を持つ手続きが作られglobalのmake-withdrawと言う名前が拘束される。

make-withdrawを呼び出すと、balanceを保存したフレームが作られ、その環境下でmake-withdrawの中身が評価される。この中身もlambdaなので、そのlambdaの中身とその環境でのフレーム、即ちbalanceを保存したフレームへの参照が手続きとして返される。例ではこれがW1と言うシンボルが拘束される。

W1を呼び出すと、amountを保存したフレームが作られる。その外部環境はその手続きが持っている環境なのでbalanceを保存した環境となる。この環境下でW1が指している手続きを実行すると、balanceは50に変更され、実行が終わるとamountを保存したフレームは消える。

Exercise 3.10

letをlambdaで書き換えると:

(define (make-withdraw initial-amount)
  ((lambda (balance)
     (lambda (amount)
       (if (>= balance amount)
           (begin (set! balance (- balance amount))
                  balance)
           "Insufficient funds")))
   initial-amount))
  1. make-withdrawを呼ぶとintial-amountを保存したフレームが出来、
  2. lambdaを評価して手続きを作り
  3. その手続きを呼ぶのでbalanceをinitial-amountと同じ値に拘束したフレームを作ってその中を評価する。
(define W1 (make-withdraw 100))
(W1 50)
(define W2 (make-withdraw 100))

実行の様子を追うと:
まずmake-withdrawを定義。

引数100にmake-withdrawを適用。make-withdrawの本体を評価する環境が作られる。

最初のlambdaを評価した時点で新しい手続きが作られる。

今作った手続きをinitial-amountに適用する。その為の環境が作られる。

lambdaだが評価されて新しい手続きが作られ、それにW1が拘束される。

途中で作られた手続きはどこからも参照されないので消される。
W1を50に適用する。その為の環境が作られる。

set!が実行されてbalanceの内容が書き替わる。

実行が終わり、用済みの環境は消される。

W2はW1と同様に、しかし別途環境が作られる。

3.2.4 Internal Definitions

手続きの中でdefineしたものにシンボルを拘束すると、そのシンボルはその手続きを評価している環境に保存される。
利点は

  1. 名前の衝突を避けられる
  2. 内部手続きはそれを定義した手続きの引数にアクセスする事が出来る

ここで出て来る自由変数(free variable)は、引数として与えられる変数以外の変数の事。関数f(x)の式に出て来るx以外の変数と言う意味。

Exercise 3.11

メッセージパッシング方式での環境。
accを定義

3つの手続きに関連付けられたフレームはglobalではなくE1。手続きdispatchは2カ所から参照されている事になる。
accに'dipositを送ると手続きへの参照が返って来て、それを適用する。

accに'withdrawを送ると別の手続きへの参照が返って来て、それを適用する。

make-accountをもう一度呼ぶと別の環境が作られる。