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

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

mini lisp on lisp とクロージャ

mini lisplisp 上で作った。mini lisp は define と #t #f をサポートした scheme 風の言語に設計した。lisplisp を作っているのであっという間にできた。っていうかもうなれちゃったな。何回も 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++ だと、引き継いだ環境の中のどれか。かなりクロージャに近い。