quoted? と assignment? とdefinition?
quoted? は (quote (a b c)) のように quote で始まるかどうかをチェックしている。lisp ではもっと簡単に '(a b c) と書く。こういう lisp の事情が理解できると quoted? はことさら説明するまでもない。
assignment? は set! で始まる scheme の代入(?)。すでに define されたものしか set! できない。scheme の使い方としては
(set! v 23)
いまある環境に set! したい。環境は入れ子になっている。まず一番下の環境に v があるかを確認する。なければ上の環境を検索し、最終的には大本の環境に行き着く。そこまでやって環境の中に v がなければ scheme としてはエラーだ。common lisp だと setf でなければ自動的に変数が作られるが、scheme ではそうしていない。
各環境は例によって変数のシンボルと値がペアーになっているのでそれを scan することになる。SICP での関数は eval-assignment となっているが、その実体のほとんどは set-variable-value1! だ。common lisp では defun の中に defun を書けないので labels を使った。
(defun set-variable-value! (var val 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)) (setf (car vals) val)) (t (scan (cdr vars) (cdr vals) env))))) (env-loop env)))
かなりまどろっこしい。というか環境の構造を知らないとこの関数を読み解くことが出来ない。 もっと簡単に hashtable を使った方が common lisp としてもプログラムとしてもすっきりするだろう(NIY)。
definition? は define ではじまる scheme の定義をするための関数。変数と関数を登録できる。scheme の使い方として変数は
(define v '(a b c))
とすればよい。関数はちょっと複雑だ。一番フォーマルなのは
(define f (lambda (a0 a1) (+ a0 a1)))
のように lambda と明示的にするもの。さて、この define を scheme-eval で評価させると、lambda で始まる式は一度評価される。正確には今、定義しようとしている value 部が一度評価される。つまり '(a b c) なら (a b c) となり quote がとれる。lambda ではじまるなら lambda 式を評価しようとする。したがって、scheme-eval には lambda を評価するための lambda? によるチェックがある。
lambda で始まった式は評価されると単純に make-procedure で procedure に変換される。結果として、f という変数は lambda 式を変換した procedure として記憶される。
例えば、scheme-eval で f を使うとき次のような評価の順をたどる。
(f 3 5) ; f が評価されて procedure に置き換わる ((procedure ....) 3 5) ; procedure が評価される。その実体は (+ a0 a1) 8
(defun define-variable! (var val env) (let ((frame (first-frame env))) (labels ((scan (vars vals) ;(debug-trace (format t "~a ~a ~%" vars vals)) (cond ((null vars) (add-binding-to-frame! var val frame)) ((eq var (car vars)) (setf (car vals) val)) (t (scan (cdr vars) (cdr vals)))))) (scan (frame-variables frame) (frame-values frame)))))