レキシカルバインディングは、モダンなEmacs Lisp方言でのみ利用可能です。(Lisp方言の選択を参照してください。)レキシカルにバインドされた変数にはレキシカルスコープがあり、これはその変数へのあらゆる参照が、テキスト上でバインド構文内に位置していなければならないことを意味します。以下に例を示します。
(let ((x 1)) ;xis lexically bound. (+ x 3)) ⇒ 4 (defun getx () x) ;xis used free in this function. (let ((x 1)) ;xis lexically bound. (getx)) error→ Symbol's value as variable is void: x
ここでは、変数 x にグローバル値は存在しません。let フォーム内で字句的に束縛されると、その let
フォームのテキスト範囲内で使用できます。しかし、let フォームから呼び出された getx 関数内からは使用できません。というのも、getx の関数定義は let
フォーム自体の外側で記述されているためです。
レキシカルバインディングの仕組みは以下の通りです。各バインディング構文はレキシカル環境を定義し、その構文内でバインドされる変数とそのローカル値を指定します。Lisp評価器が変数の現在の値を求める際は、まずレキシカル環境を確認します。そこで変数が指定されていない場合は、動的な値が格納されているシンボルの値セルを確認します。
レキシカルバインディングの有効範囲は不定です。バインディング構文の実行が終了した後でも、そのレキシカル環境はクロージャと呼ばれるLispオブジェクトとして「保持」されることがあります。クロージャは、レキシカルバインディングを有効にして名前付き関数または無名関数を定義した際に作成されます。詳細はクロージャを参照してください。
クロージャが関数として呼び出されると、その定義内にあるレキシカル変数への参照はすべて、保持されたレキシカル環境を使用します。以下に例を示します。
(defvar my-ticker nil) ; We will use this dynamically bound ; variable to store a closure. (let ((x 0)) ;xis lexically bound. (setq my-ticker (lambda () (setq x (1+ x))))) ⇒ #f(lambda () [(x 0)] (setq x (1+ x))) (funcall my-ticker) ⇒ 1 (funcall my-ticker) ⇒ 2 (funcall my-ticker) ⇒ 3 x ; Note thatxhas no global value. error→ Symbol's value as variable is void: x
ここでは、let の束縛により、変数 x
が局所的に 0 に束縛されるレキシカル環境が定義されています。この束縛構文の中で、x を 1
インクリメントし、その増分後の値を返すラムダ式を定義します。このラムダ式は自動的にクロージャに変換され、let
の束縛構文を抜けた後も、そのレキシカル環境は存続し続けます。クロージャを評価するたびに、そのレキシカル環境における x の束縛を用いて x がインクリメントされます。
なお、シンボルオブジェクト自体に紐付いている動的変数とは異なり、字句変数とシンボルの関係はインタプリタ(またはコンパイラ)内にのみ存在することに注意してください。したがって、シンボル引数を取る関数(symbol-value、boundp、setなど)は、変数の動的バインディング(すなわち、そのシンボルの値セルの内容)のみを取得または変更することができます。
また、変数を「特殊」として宣言できる点にも注意してください。この場合、let
バインディングのような新しいバインディングであっても、動的バインディングが使用されます。変数の宣言方法に応じて、グローバルに、単一のファイルに対して、あるいはファイルの一部に対して特殊として宣言できます。詳細については、動的バインディングを参照してください。