現代のLisp方言では、特殊変数(後述)および旧Lisp方言のすべての変数において、ローカル変数の束縛は動的です。(Lisp方言の選択を参照してください。)動的な変数束縛には用途もありますが、一般的に字句的な束縛よりもエラーが発生しやすく効率も劣り、コンパイラが動的束縛を用いたコードの誤りを検出する能力も低くなります。
変数が動的にバインドされている場合、Lispプログラムの実行中の任意の時点におけるその変数の現在のバインドは、単にそのシンボルに対して直近に作成された動的ローカルバインドであり、そのようなローカルバインドが存在しない場合はグローバルバインドとなる。
動的束縛は、以下の例に示すように、動的なスコープと範囲を持ちます。
(defvar x -99) ;xreceives an initial value of −99. (defun getx () x) ;xis used free in this function. (let ((x 1)) ;xis dynamically bound. (getx)) ⇒ 1 ;; After theletform finishes,xreverts to its ;; previous value, which is −99. (getx) ⇒ -99
関数 getx は x
を参照します。これは、その defun 構文自体の中に x の束縛が存在しないという意味で、フリーな参照です。x が(動的に)束縛されている let フォーム内から
getx を呼び出すと、ローカル値(すなわち 1)が取得されます。一方、let フォームの外から getx
を呼び出すと、グローバル値(すなわち −99)が取得されます。
以下に、setq を用いて動的に束縛された変数を設定する例をもう一つ示します。
(defvar x -99) ;xreceives an initial value of −99. (defun addx () (setq x (1+ x))) ; Add 1 toxand return its new value. (let ((x 1)) (addx) (addx)) ⇒ 3 ; The twoaddxcalls add toxtwice. ;; After theletform finishes,xreverts to its ;; previous value, which is −99. (addx) ⇒ -98
字句束縛が有効であっても、特定の変数は引き続き動的に束縛されます。これらは特殊変数と呼ばれます。defvar、defcustom、またはdefconstで定義されたすべての変数は特殊変数です(グローバル変数の定義を参照)。それ以外のすべての変数は字句束縛の対象となります。
値を指定せずに defvar
を使用することで、他の箇所では字句的にバインドしつつ、特定のファイル内、あるいはファイルの一部だけで変数を動的にバインドすることが可能です。例えば:
(let (_) (defvar x) ; Let-bindings ofxwill be dynamic within this let. (let ((x -99)) ; This is a dynamic binding ofx. (defun get-dynamic-x () x))) (let ((x 'lexical)) ; This is a lexical binding ofx. (defun get-lexical-x () x)) (let (_) (defvar x) (let ((x 'dynamic)) (list (get-lexical-x) (get-dynamic-x)))) ⇒ (lexical dynamic)
この関数は、symbol が特殊変数(すなわち、defvar、defcustom、または defconst の変数定義を持つ)である場合に non-nil を返します。それ以外の場合、戻り値は nil
となります。
なお、これは関数であるため、恒久的に特殊な変数に対してのみ non-nil
を返すことができ、現在の字句スコープ内でのみ特殊な変数に対しては返せない点に注意してください。
関数の仮引数として特殊変数を使用することはサポートされていません。
動的束縛は、Emacs Lispにおいてシンプルな方法で実装されています。各シンボルには値セルがあり、そこで現在の動的値(あるいは値がないこと)が指定されます。シンボルの構成要素を参照してください。シンボルに動的なローカル束縛が与えられると、Emacsは値セルの内容(あるいはその内容がないこと)をスタックに記録し、新しいローカル値を値セルに格納します。束縛構文の実行が完了すると、Emacsはスタックから古い値をポップし、それを値セルに格納します。