[SICP] variable?
scheme の実装で変数をどう扱うか?という話だ。変数はシンボルなので symbolp でチェックしている。簡単だ、、、一方、その評価は簡単じゃない。
まず、仕様として環境が入れ子になっている点がある。さらに、各環境で変数に関連付けされている値がある。この値を拾ってくるのが評価になる。オリジナルでは一つの環境が、変数と値のペアーになっている(ペアーの列ではない)。
'((a b c) 1 2 3)
といった具合だ。
よーくみると列のペアーにもなっていない(以前そう思っていたのは勘違いか、、、自分で作っても後でわからなくなるぞ)
さらに環境が列になっている。
(環境n 環境n-1 .... 環境0)
この環境に対しある変数の値をとるには
(defun lookup-variable-value (var env) (labels ((env-loop (env) (if (eq env the-empty-environment) (scheme-error "Unbound variable" var) (let ((frame (first-frame env))) (scan (frame-variables frame) (frame-values frame) env )))) (scan (vars vals env) (cond ((null vars) (env-loop (enclosing-environment env))) ((eq var (car vars)) (car vals)) (t (scan (cdr vars) (cdr vals) env))))) (env-loop env)))
でよい。(1回書いているな、、、)
なるほど、、、
(defun frame-values (frame) (cdr frame))
としたいから環境はそういう形になっている。最初に絵に書こうよ。>自分。もし列のペアーなら cdr ではなく cadr になる。一個カッコを減らしている、、、
C でどうする?
最初、C++ で実装し、Environment なる特別な class を”作って”しまった。途中まではそれでもよかった。しかし、ガーベージコレクションでつまずいた。一度、作りこんでしまったので Environment なる特別な class を lisp 風のにするには時間がかかった。C で書く人は最初にガーベージコレクションを書きましょう。
結局 vm-scheme と同じようなつくりにした。通常 Atom と非 Atom は union で共通化されて 4 byte にする。下3ビットをタグにする。3ビットが0ならそれはポインタだということにする。したがって、Cell は 8バイトバウンダリになる。(絵が欲しいよ)
領域は cell 単位で管理され2つの union が連続して並ぶ。car と cdr だ。しかし、これは cell じゃなくて非 Atom のこともある。このとき car 部には type が入る。環境は Environment の type をもつ。cdr 部はポインターでそのさきに Cell があり Cell は特別に次の情報を持つ。car が Environment へのポインタ、cdr は next Cell へのポインタ。さらに、next Cell は car 部に変数のトップへの Cell。cdr は nil。さらにトップは ( key . value ) の組み合わせペアーで cdr は次のペアー。cdr に nil がはいるとおしまい。言葉じゃ無理だろ。絵を書こうよ>自分。
こんな感じかな。(自信なし)
'(enviroment (next-environment ((a 1) (b 2) (c 3))))
C++ で延々とそれをおっている。その前に GC を実装した方がいい。
C# でどうする?
C# では思い切って class にしちゃったから、GC の実装必要ない。素晴らしい(のか素晴らしくないのか)。変数を格納する方法も単純に連想配列を用いればよい。環境の入れ子は必要。やけに簡単だな、、、
実行
実行しようとすると、環境が必要になる。ここはなんと手で環境を入力。
(setf the-global-environment `(((a b c) 1 2 3))))