CLパッケージは、Common Lispのlet
フォームにより厳密に従う
下記のマクロを定義する:
このフォームは、確立する束縛が純粋にレキシカルである点以外はlet
とほぼ似ている。レキシカルな束縛はCのような言語の局所変数と
類似している: 物理的にlexical-let
(マクロ展開後)の本体内の
コードだけが束縛変数を参照してよい。
(setq a 5) (defun foo (b) (+ a b)) (let ((a 2)) (foo a)) ⇒ 4 (lexical-let ((a 2)) (foo a)) ⇒ 7
この例では、a
の正規のlet
束縛は実際グローバル変数a
を一時的に変更するので、foo
は2へのa
の束縛を
見ることができる。しかしlexical-let
は実際本体内で使うために別の
局所変数a
を作り、同名のグローバル変数にどんな影響もない。
レキシカルな束縛の最も重要な使用法はクロージャを作ることである。 クロージャは外のレキシカル変数を参照する関数オブジェクトである。 たとえば:
(defun make-adder (n) (lexical-let ((n n)) (function (lambda (m) (+ n m))))) (setq add17 (make-adder 17)) (funcall add17 4) ⇒ 21
呼び出し(make-adder 17)
は引数に17を加える関数オブジェクトを
戻す。let
がlexical-let
の代わりに使われる場合、
関数オブジェクトはmake-adder
自身の呼び出しの間だけ17へ
束縛されているグローバルのn
を参照するだろう。
(defun make-counter () (lexical-let ((n 0)) (function* (lambda (&optional (m 1)) (incf n m))))) (setq count-1 (make-counter)) (funcall count-1 3) ⇒ 3 (funcall count-1 14) ⇒ 17 (setq count-2 (make-counter)) (funcall count-2 5) ⇒ 5 (funcall count-1 2) ⇒ 19 (funcall count-2) ⇒ 6
ここでmake-counter
への各呼び出しは別の局所変数n
を作る。
それは戻される関数オブジェクト用の私的なカウンターとして働く。
その上で閉じられたレキシカル変数は、ちょうど他のLispオブジェクトと
同様にそれへの最後の参照がなくなるまで存続する。たとえば、
count-2
は変数n
のインスタンスを参照する関数オブジェクトを
参照する; これはその変数への唯一の参照なので、
(setq count-2 nil)
の後にはガベジコレクタはn
のこの
インスタンスを削除できるだろう。もちろん、lexical-let
が実際には
クロージャをまったく作らない場合、レキシカル変数はlexical-let
が
戻るとすぐに自由である。
多くのクロージャはそれらが参照する束縛のエクステントの間だけ使われる;
これらはLisp語法では“downward funargs”として知られている。クロージャ
がこのように使われる場合、正規のEmacs Lisp動的束縛で十分であり、
lexical-let
クロージャより効率的だろう。
(defun add-to-list (x list) (mapcar (function (lambda (y) (+ x y))) list)) (add-to-list 7 '(1 2 5)) ⇒ (8 9 12)
このラムダはx
がまだ束縛されているときにのみ使われているので、
そのために真のクロージャを作ることは必要ではない。
名前付きクロージャを作るためにlexical-let
の内部でdefun
や
flet
を使うことができる。いくつかのクロージャが1個の
lexical-let
の本体で作られる場合、それらはすべてレキシカル変数の
同じインスタンスの上で閉じている。
lexical-let
フォームはCommon Lispへの拡張である。真のCommon Lisp
では、すべての束縛はそうではないと宣言されない限りレキシカルである。
このフォームはちょうどlexical-let
と似ているが、束縛は
let*
の方法のように順次に作られる点が異なる。