環境フレームモデル
置き換えモデルではbeginやset!が絡んだ時に正しい値にならなかった。だけど、環境フレームモデルでは正しく計算できるらしい。
だい〜ぶお利口、置き換えモデルよりいくらかCOOL(by 宮崎吐夢
環境フレームモデルでも「環境」を用いるが、置き換えモデルのそれとはちょっと違う。
- 環境フレーム
- 置き換えモデルで出てきた「環境」と同じ。束縛の表。
- 環境
- 環境フレームモデルでは、複数の環境フレームを用いるが、環境フレーム全体のこと。
では、どのようにして複数の表を使うのだろうか。
環境フレームモデルでは、
- 関数適用((lambda (arg) body) param)にさしかかったら
- 新たに作成した環境フレームにargとparamからなる束縛を追加し、その環境フレームを環境にプッシュ
- 上記で拡張した環境でbodyを評価
- プッシュした環境フレームをポップ
- defineで、現在の環境フレームに束縛を追加
という感じ。環境フレームモデルも作用順序評価なのは同じ。
名前を探さなくちゃいけない時は、新しい方の環境フレームから探していって、最初に見つかった束縛を使う。
では、本のコード例をやってみる。
(((lambda (y) (lambda (x) (begin (set! y (+ x y)) y))) 10) 1)
リストを評価しようとしているので、関数適用。よって、
- 引数1を評価し、整数1を取得
- 手続きを作成
作成する手続きは以下を評価すると得られる。...(a)
((lambda (y) (lambda (x) (begin (set! y (+ x y)) y))) 10)
これも関数適用。よって、
- 引数10を評価し、整数10を取得
- lambda式を評価し、値としての手続き作成。
- できた手続きを引数10に適用、つまり...
- 新たな環境フレーム1を作成し、そこにyと10を結び付ける束縛を追加。環境フレーム1をプッシュ。
- 拡張された環境の下で、(lambda (x) (begin ... ))を評価し、手続きを取得。
- プッシュした環境フレーム1をポップ
これで、(a)の手続きができたので、
- 出来た手続きを引数1に適用、つまり...
- 新たな環境フレーム2を作成し、そこにxと1を結び付ける束縛を追加。環境フレーム2をプッシュ。
- 拡張された環境の下で、(begin (set! y (+ x y)) y)を評価...(b)
- プッシュした環境フレーム2をポップ
beginとset!と+はプリミティブなので中身はブラックボックスとしよう。とくにbeginやset!はマクロなので手続きではないので詳しい手順は分からないけど、
- 環境フレーム1のyに関する束縛のうち、値の方を(+ x y)の評価結果で書き換える
- yの値(書き換え後)を返す
ってことを、この順序で行うはずので、そういうことで納得しておく。
一つ気になる点が。
(b)の時点で、yの束縛を保持した環境フレーム(つまり環境フレーム1)は既にポップされているから、yという名前を検索しても見つからなくね?
にもかかわらず、(+ x y)や(set! y (+ x y))ちゃんと評価できるのはナゼ?
評価しようとしている式のyが環境フレーム1の束縛への参照を持っているので、まだ環境フレームは1はGCに破棄されてないから...かな?
150ページの脚注
ポップされた環境フレームは、参照がある限りずっと残ります。どこからも参照されなくなった後に、ガベージコレクタによって回収されます。
が意味するところはこういうことかな?
訂正?
マクロとはユーザ定義の構文らしいので、beginやset!はマクロとは呼ばないかもです。