Go to the first, previous, next, last section, table of contents.

スコープと Extent

変数は、indefinite スコープと呼ばれるものと、 dynamic extent と呼ばれるものを 持つよう定義されています。スコープとは、その変数をアクセスすることのできる (コード中の)場所を指し、 extent とはその変数をアクセスすることのできる時間(枠) を指します。これは GNU Emacs Lisp に特徴的な部分であるため (Pascal, C, Algol, Fortran 等は全てレキシカルにスコープを定めます)、これに関しては(多少)コメント しておく必要があります。

indefinite スコープとは、どの関数からでも(与えられた変数の)バインディングをア クセスすることができることを意味します。関数 fn1 でバインドされた変数 x は、 (fn2 のコードが fn1 のコードとは完全に分離されていても) fn2 からアクセスするこ とができます。

次に示す例において、(訳注:この記述だけからは) "関数 fn2 が (関数 fn1 でバイン ドされている)変数 x をアクセスできるか否か" を判断できません。それは、"fn2 を 呼ぶ関数(訳注:例えば) foo で x がバインドされたか否か" によります。レキシカル にスコープが定められる言語では、fn2 から x をアクセスすることはできません。

      (defun fn1 (x)
         (foo 5))
      (defun fn2 ()
        (list x))

foo の定義が (defun foo (y) (fn2)) である場合、fn2 は x をアクセスすることがで きます。 foo の定義が (defun foo (x) (fn2)) である場合、fn2 は (代りに) foo で バインドされた x をアクセスします。 (訳注:このような場合) foo の x は fn1 の x を隠す(訳注:shadow)と言います。もちろん、(fn1 や foo を通してではなく) fn2 を直接コールした時、(x の値がどこにも存在しない場合) x を用いると、void- variable エラーが出されます。

extent は、(プログラムの実行において) ある変数が有効である時間(枠)のことを指し ます。 GNU Emacs Lisp では、(他の多くの言語におけるように) ローカル変数はそれ をバインドするフォームの実行中でのみ有効です。

(dynamic extent とは別の) indefinite extent と呼ばれるものは、あるフォームの実 行終了後も、それにバインドされた変数をアクセスできる関数を定義できることを意味 します。 GNU Emacs は indefinite extent をサポートしません。以下に示す例では、 関数 make-add は n を(それ自身の)アーギュメント m に足す関数を返します。 (GNU Emacs Lisp では) make-add の呼び出し終了後、変数 n は(すでに)有効ではないため これは思うようには動きません(訳注:??)。 (Common Lisp のような)いくつかの言語 では、これは可能です。

      (defun make-add (n)
          (function (lambda (m) (+ n m))))
      => make-add
      (setq add2 (make-add 2))
      => (lambda (m) (+ n m))
      (funcall add2 4)
      -> ERROE: Symbol's value as variable is void: n

このように、変数のアクセス可能性を定義するのは、スコープと extent の組み合わせ です。 dynamic extent と indefinite スコープの組み合わせは dynamic スコープも しくは dynamic バインディングと呼ばれます。 GNU Emacs Lisp では dynamic スコー プは(うまく) 単純なスタック操作に変換されます。ローカル変数はスタックにバイン ドされます。変数はスタックに残り、(それが同じ名前を持つ他の変数で隠されない限

り) evaluator はそれを見ます。

"ある関数でバインドされた変数を別の関数からアクセスすることができる" という点 に関しては、 "(その目的を達成する)別の方法が存在しない場合にのみ用いる" という のが、プログラミングを作る上での良い心構えとなります。

値セルに void でない値を持つシンボルは、 (同じシンボルがローカル変数としてバイ ンドされてない場合に限り) グローバル変数として(どのようなフォームからでも)アク セスすることができます。

Function: boundp symbol

この関数は、シンボルが void でない場合 (ローカルバインディングを持つかグ ローバル値を持つ場合)、t を返します。そうでない場合、nil を返します。 makunbound の関連で、直感的でない部分(訳注:counter-intuitive case)が存在 しています (セクション 9.1 [makunbound]、ページ 73 参照)。

  (boundp 'abracadabra)           ;初めはバインドされていない
  => nil
  (let ((abracadabra 5))
       (boundp 'abracadabra))
  => t
  (boundp 'abracadabra)           ;まだ グローバルにはバインドされていない
  => nil
  (setq abracsdabra 5)
  => 5
  (boundp 'abracadabra)
  => t

Function: makunbound symbol

この関数は、 symbol の値を(シンボルの void ではない) void に設定し、 symbol を変数として用いるとエラーが出されるようにします。 symbol がバイン

ドされている場合、 (これは)そのバインディングを無効にします。この際、前の バインディングを見せるようにはしません。

この関数は、(通常)グローバル値を unbound するためにのみ用いられ、このこと 自体ごくまれにしか行なわれません[この関数を使いたいと思われることは(たぶ ん) ないでしょう]。

  (setq x 1)
       => 1
  (makeunbound 'x)
       => x
  x
       -> ERROR: Symbol's value as variable is void.
  (setq x 1)
       => 1
  (let ((x 2))
    (makeunbound 'x) x)
       -> ERROR: Symbol's value as variable is void.
  (let ((x 2))
    (let ((x 3))
      (makeunbound 'x)
      x))
       -> ERROR: Symbol's value as variable is void.
  (let ((x 2))
    (let ((x 3))
      (makeunbound 'x))
      x)
       => 2


Go to the first, previous, next, last section, table of contents.