mini lisp on lisp とクロージャ
mini lisp を lisp 上で作った。mini lisp は define と #t #f をサポートした scheme 風の言語に設計した。lisp で lisp を作っているのであっという間にできた。っていうかもうなれちゃったな。何回も scheme つくっているので。
今回はクロージャを実装した。いままで、クロージャを作っていなかったので、作った lisp はダイナミック・スコープだった。ダイナミック・スコープの何がめんどくさいって、字面だけじゃ、その変数がどこで使われているかわからない点。
そこで、スタティックにするために(つまりレキシカルにするために)クロージャを導入した。私の場合、単純に関数と環境をあわせたものをクロージャと呼んでいる。
scheme 相当、わかりづらいけど、、、、
(define xxx 3) (define (disp a b c) (display a) (display b) (display c)) (define fg (let ((x 0) (y 0) (g '())) (let ((f (lambda (a) (disp x y a) (set! x a))) (mkg (lambda () (define g (lambda (z) (disp x y z) (set! y z))) (display "run-mkg") g))) (list f mkg)))) (define f (car fg)) (f 3) (define mkg (cadr fg)) (define g (mkg)) (g 5)
まぁとにかく、ぐちゃぐちゃかいているが、レキシカル・スコープになる(字面通り)。プログラム自信は 2つの内部関数を定義している。これは fg で外に出される。この2つの内部関数は x y g を束縛している。これはまぁ、レキシカル・スコープだからそうでしょうと。
この時点で g という関数は存在しない。mkg を実行することで g は定義される。mkg の延長で定義されるので、mkg のレキシカル・スコープを引き継ぐ。mkg がどう呼ばれてきたかはあまり関係ない。mkg の環境を引き継ぐ。結果として g は字面通り x y g を束縛する。
これがダイナミック・スコープだと(emacs lisp) mkg がどう呼ばれたかが問題になる。呼ばれるたびにその参照する環境が変わっちゃう。ダイナミック・スコープはlisp のスペシャル変数でできる。
参考までに書くと C ではこんな問題はそもそもおこらない。関数の中で使っているけど、関数の中で宣言されていない変数は多分グローバル変数だ。
C++ だと、引き継いだ環境の中のどれか。かなりクロージャに近い。