(defstruct closure params code env)レキシカルクロージャというのは,この関数の定義を作った時点のレキシカル スコープをもつ変数の環境を保存するためのものです.そのため, interp-specialformは以下のようになります. lambdaの処理は構造体のクロージャを返すことになります.
(defun interp-specialform (name params env) (case name (quote (car params)) (begin (if (null params) nil (car (last (mapcar #'(lambda (v) (interp v env)) params))))) (set! (set-var! (car params) (interp (cadr params) env) env)) (if (if (interp (car params) env) (interp (cadr params) env) (interp (caddr params) env))) (lambda (make-closure :params (car params) :code (cons 'begin (cdr params)) :env env))))これに伴って,手続きを評価する interp-call の中での処理は,この構造体データであるクロージャが 与えられた場合には,Lispのapplyでは処理できないので, そのクロージャの中にある環境を考慮してそのクロージャの 中の手続きコードをinterpするという形になります.
(defun interp-call (call env) (let ((proc (interp (car call) env)) (args (mapcar #'(lambda (v) (interp v env)) (cdr call)))) (if (closure-p proc) (interp (closure-code proc) (extend-env (closure-params proc) args (closure-env proc))) (apply proc args))))という具合いの記述になります. つまり,interp-call に渡されるフォーム(call)が,lambdaフォームで作られ たクロージャに引数を適用する形である場合には,そのクロージャの中に保存 されている環境(closure-env)に,そのクロージャへ渡された実引数argsとク ロージャの中に宣言されている引数(closure-params)とのバインディングを追 加した環境を作り,その環境の中でクロージャの中にある関数定義をinterpす るという形になります. そのフォーム(call)がクロージャを呼び出す場合でなく,car, cdr のように, init-scheme-interpreter の中で前もって定義されている手続きの場合には, CommonLisp の関数定義をそのまま使っているので,CommonLisp の apply を 使って評価するということになります. 実行例は以下のようになります.
<cl> (scheme-interpreter) ==> (define (fact n) (if (< n 2) 1 (* (fact (- n 1)) n))) FACT ==> fact #s(CLOSURE :PARAMS (N) :CODE (BEGIN (IF (< N 2) 1 (* (FACT (- N 1)) N))) :ENV NIL) ==> (fact 5) 120 ==> (define (table f start end) (if (<= start end) (begin (write (list start (f start))) (newline) (table f (+ start 1) end)))) TABLE ==> table #s(CLOSURE :PARAMS (F START END) :CODE (BEGIN (IF (<= START END) (BEGIN (WRITE (LIST START (F START))) (NEWLINE) (TABLE F (+ START 1) END)))) :ENV NIL) ==> (table fact 1 5) (1 1) (2 2) (3 6) (4 24) (5 120) NIL ==> (table (lambda (x) (* x x x)) 5 10) (5 125) (6 216) (7 343) (8 512) (9 729) (10 1000) NIL関数定義 fact の実体がクロージャを示す構造体になっています.:params ス ロットは fact 関数の引数で,:code は Scheme での定義であり,begin フォー ムとなっています.:env は fact の定義を行なった環境にレキシカル変数が ないために nil になっています.
==> (define a 10) A ==> (let ((a 1)) (let ((b (+ a 3))) (list a b))) (1 4) ==> a 10