新千葉 ガーベージ・コレクション

FPGA マガジンやインターフェースで書けなかったこと等をちょぼちょぼ書いてます。@ryos36

[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))))