ローカル変数のバインディングは現代的なLisp方言ではスペシャル変数にたいして(以下参照)、古いLisp方言ではすべての変数にたいしてダイナミックです(Lisp方言の選択を参照)。ダイナミックな変数バインディングにも用途はあるものの、レキシカルバインディングに比べて一般的にはエラーが発生しやすく効率に劣り、ダイナミックバインディングを使用したコードではコンパイラーが誤りを発見しにくくなります。
ある変数がダイナミックにバインドされていると、Lispプログラムの実行における任意のポイントでのカレントバインディングは、単にそのシンボルにたいしてもっとも最近作成されたダイナミックなローカルバインディング、またはそのようなローカルバインディングが存在しなければグローバルバインディングになります。
以下の例のように、ダイナミックバインディングはダイナミックスコープとダイナミックエクステントをもちます:
(defvar x -99) ;x
は初期値として−99を受け取る (defun getx () x) ; この関数内ではx
は自由に使用される (let ((x 1)) ;x
はダイナミックにバインドされている (getx)) ⇒ 1 ;;let
フォームが終了した後に ;;x
は前の値−99にリバートされる (getx) ⇒ -99
関数getx
はx
を参照します。defun
構文自体の中にx
にたいするバインディングが存在しないという意味において、これはフリーな参照です。x
が(ダイナミックに)バインドされているlet
フォーム内からgetx
を呼び出すと、ローカル値(つまり1)が取得されます。しかしその後let
フォームの外側からgetx
を呼び出すと、グローバル値(つまり−99)が取得されます。
以下はsetq
を使用してダイナミックに変数をバインドする例です:
(defvar x -99) ;x
は初期値として−99を受け取る (defun addx () (setq x (1+ x))) ;x
に1加算して新しい値をリターンする (let ((x 1)) (addx) (addx)) ⇒ 3 ;addx
を2回呼び出すとx
に2回加算される ;;let
フォームが終了した後に ;;x
は前の値−99にリバートされる (addx) ⇒ -98
レキシカルバインディングが有効な場合でも、特定の変数はダイナミックにバインドされたままです。これらはスペシャル変数(special
variable)と呼ばれます。defvar
、defcustom
、defconst
で定義されたすべての変数はスペシャル変数です(グローバル変数の定義を参照)。その他のすべての変数はレキシカルバインディングの対象になります。
値なしでdefvar
を使用することにより、他の場所ではレキシカルにバインドされている状態のまま、単一ファイルやファイルの一部だけで変数をダイナミックにバインドすることが可能になります。たとえば:
(let (_) (defvar x) ;x
へのletバインドはこのlet内ではダイナミック (let ((x -99)) ; これはx
のダイナミックバインド (defun get-dynamic-x () x))) (let ((x 'lexical)) ; これはx
のレキシカルバインド (defun get-lexical-x () x)) (let (_) (defvar x) (let ((x 'dynamic)) (list (get-lexical-x) (get-dynamic-x)))) ⇒ (lexical dynamic)
この関数はsymbolがスペシャル変数(つまり変数がdefvar
、defcustom
、defconst
による定義をもつ)なら非nil
をリターンする。、それ以外ならリターン値はnil
。
これは関数なので永続的にスペシャルな変数には非nil
をリターンできるが、カレントレキシカルスコープでのみスペシャルな変数では異なることに注意。
関数内の正式な引数としてのスペシャル変数の使用はサポートされていません。
Emacs Lispでのダイナミックバインディングは、シンプルな方法で実装されています。シンボルはそれぞれ、シンボルのカレントのダイナミック値(または値の不在)を指定する値セルをもちます。シンボルの構成要素を参照してください。あるシンボルがダイナミックなローカル値を与えられたとき、Emacsは値セルの内容(または値の不在)をスタックに記録して、新しいローカル値を値セルに格納します。バインディング構文が実行を終えたとき、Emacsはスタックから古い値をpopして値セルにそれを配置します。