Next: , Previous: , Up: 変数のバインディングのスコーピングルール   [Contents][Index]


12.10.3 レキシカルバインディング

Emacsのバージョン24.1からオプションの機能としてレキシカルバインディングが導入されました。わたしたちはこの機能の重要性が時とともに増加することを期待します。レキシカルバインディングは最適化の機会をより広げるので、この機能を使用するプログラムはおそらく将来のEmacsバージョンで高速に実行されるようになるでしょう。レキシカルバインディングは、バージョン26.1のEmacsで追加した並列性(concurrency)とも互換があります。

レキシカルにバインドされた変数はレキシカルスコープ(lexical scope)をもちます。これはその変数にたいする参照が、そのバインディング構文内にテキスト的に配置されなければならないことを意味しています。以下は例です (実際にレキシカルバインディングを有功にする方法は、レキシカルバインディングの使用を参照のこと):

(let ((x 1))    ; xはレキシカルにバインドされる
  (+ x 3))
     ⇒ 4

(defun getx ()
  x)            ; この関数内ではxは自由に使用される

(let ((x 1))    ; xはレキシカルにバインドされる
  (getx))
error→ Symbol's value as variable is void: x

ここではxはグローバル値をもちません。letフォーム内でレキシカルにバインドされたとき、この変数はletのテキスト境界内で使用できます。しかしこのlet内から呼び出されるgetx関数からは、getxの関数定義がletフォームの外側なので使用することができません

レキシカルバインディングが機能する方法を説明します。バインディング構文はぞれぞれ、その構文内でローカル値にバインドする変数を指定する、レキシカル環境(lexical environment)を定義します。Lispの評価機能(Lisp evaluator)が、ある変数のカレント値を得たいときは、最初にレキシカル環境内を探します。そこで変数が指定されていなければ、ダイナミック値が格納されるシンボルの値セルを探します。

(レキシカル環境は内部的には、通常はシンボルと値のペアーによるコンスセルをメンバーとするリストだが、一部のメンバーはコンスセルではなくシンボルでもよい。このリストにおけるシンボルは、そのシンボルの変数はローカルではダイナミックにバインドされているとみなすよう宣言されたレキシカル環境を意味している。このリストはフォームを評価するためのレキシカル環境を指定するために、eval関数の2番目の引数として渡すことができる。evalについてを参照のこと。しかしほとんどのEmacs Lispプログラムは、この方法で直接レキシカル環境を使用するべきではない。デバッガーのような特化されたプログラムだけが使用すること。)

レキシカルバインディングは不定エクステント(indefinite extent)をもちます。バインディング構造が終了した後でも、そのレキシカル環境はクロージャ(closures)と呼ばれるLispオブジェクト内に“保持”されるかもしれません。クロージャはレキシカルバインディングが有効な、名前つきまたは無名(anonymous)の関数が作成されたときに作成されます。詳細はクロージャを参照してください。

クロージャが関数として呼び出されたとき、その関数の定義内のレキシカル変数にたいする任意の参照は、維持されたレキシカル環境を使用します。以下は例です:

(defvar my-ticker nil)   ; クロージャを格納するために
                         ; この変数を使用する

(let ((x 0))             ; xはレキシカルにバインドされる
  (setq my-ticker (lambda ()
                    (setq x (1+ x)))))
    ⇒ (closure ((x . 0)) ()
          (setq x (1+ x)))

(funcall my-ticker)
    ⇒ 1

(funcall my-ticker)
    ⇒ 2

(funcall my-ticker)
    ⇒ 3

x                        ; xはグローバル値をもたないことに注意
error→ Symbol's value as variable is void: x

letバインディングは、内部に変数xをもつレキシカル環境を定義して、変数は0にローカルにバインドされます。このバインディング構文内でxを1増加して、増加された値をリターンするクロージャを定義しています。このラムダ式は自動的にクロージャとなり、たとえlet構文を抜けた後でも、その内部ではレキシカル環境が存続します。クロージャを評価するときは、毎回レキシカル環境内のxのバインディングが使用されて、xが加算されます。

シンボルオブジェクト自体に束縛されるダイナミック変数と異なり、レキシカル変数とシンボルの関係はインタープリター(かコンパイラー)内にのみ存在します。したがって(symbol-valueboundpsetのような)シンボル引数を受け取る関数ができるのは、変数のダイナミックなバインディング(そのシンボルの値セルの内容)の取得と変更だけです。