[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10. 変数

変数(variable)とはプログラム内で使用される名前で、ある値を表します。ほ とんどすべてのプログラミング言語にはこのような変数があります。Lispプログラム のテキストでは、変数はシンボルの構文で記述します。

通常のプログラミング言語と異なり、Lispのプログラムは主にLispオブジェクトとし て表され、テキストとしては二次的にしか表されません。変数に使用されるLispオブ ジェクトはシンボルです。シンボル名が変数名で、変数の値はシンボルの値セルに 格納されます。シンボルは、変数として使うこととと関数名として使うこととは独立です。 See section シンボルの要素

Lispプログラムを構成するLispオブジェクトは、そのプログラムのテキスト形式を決 定します。単に、そのLispオブジェクトのリード構文であるということです。このた め、たとえばテキストによるLispプログラムの変数の記述には、その変数を表すシンボル のリード構文を使用します。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.1 グローバル変数

変数のもっとも簡単な使用方法はグローバルに(globally)使うことです。すな わち変数の値は一度に一つだけで、この値は(少なくともこの時点では)Lispシステム 全域に対して有効であるということです。値は、新しく指定するまで有効です。値が 指定されて元の値が置き換えられた場合、元の値に対する痕跡は変数内に存在しなく なります。

シンボルに対する値はsetqを用いて指定します。例を示します。

 
(setq x '(a b))

上の例では、変数xに値(a b)が指定されます。setqは 最初の引数(変数名)は評価しませんが、二番目の引数(新規の値)は評価します。

変数がいったん値を持つと、シンボルそれ自体を式として用いた参照が行なえます。 すなわち、

 
x ⇒ (a b)

ここでは、上述したようなsetq形式がすでに実行されたことを前提として います。

再度setqを行なうと、元の値が新しい値に置き換わります。

 
x
     ⇒ (a b)
(setq x 4)
     ⇒ 4
x
     ⇒ 4

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.2 変更されない変数

Emacs Lispには、nilおよびtという特別なシンボルがあり、この二つの評 価結果は常に自分自身になります。これらのシンボルは、再束縛したり値セルを変更した りすることはできません。niltを変更しようとすると、 setting-constantエラーが発生します。

 
nil ≡ 'nil
     ⇒ nil
(setq nil 500)
error--> Attempt to set constant symbol: nil

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.3 ローカル変数

グローバル変数の値は、明示的に書き換えるまで保持されます。ただし、一時的にし か存在しない、すなわちプログラムの中で局所的(ローカル)にしか存在しない変数の値を作成し た方が便利な場合もあります。このような値はローカルである(local)といい 、その場合に使用する変数をローカル変数(local variables)といいます。

たとえば、関数が呼出された場合、仮引数はローカルな値を新しく受け取り、 関数を抜けるまで保持します。let特殊形式は、指定された変数に対し新 規のローカルな値を明示的に確立します。これらの値はlet形式から抜ける まで存在しています。

ローカルな値を確立すると、その変数の直前の値(または値が存在しないという情報) が保存されます。ローカルな値のライフタイムが終わると、直前の値が復元されます 。その間、直前の値は隠されて(shadowed)おり、不可視(not visible) になっていたということができます。グローバル変数とローカル変数はどちらも隠し ておくことができます (see section スコープ)。

変数がローカルであるときにセットすると(setqなどを用いて)、ローカルの 値が置き換わります。グローバルの値や、隠されているローカル値の直前の値は変更 されません。この動作を示すために、局所的な束縛(local binding)について 、ローカル値と同じように説明しましょう。

局所的な束縛とは、局所的な値を保持している概念的な場所です。関数や、let のような特殊形式を開始すると、局所的な束縛が作成されます。関数を抜けると、 letは局所的な束縛を解除します。局所的な束縛が続いている間、変数の値は その中に格納されています。局所的な束縛があるときに setqsetを 使用すると、その局所的な束縛には別の値が格納されます。新規に束縛が作成される わけではありません。

グローバル束縛(global binding) についても述べておきましょう。ここには、 (概念的に) グローバル変数が保持されます。

変数には、局所的な束縛を同時に複数持たせることもできます(たとえば、ネストした let形式が束縛しているような場合)。このような場合、存在している最新の 局所的な束縛が、その変数の現在の束縛(current binding)になります。これを 動的スコーピング(dynamic scoping)といいます。変数束縛のスコーピングに関する規則を参照してく ださい。局所的な束縛がない場合、その変数のグローバル束縛が現在の束縛になりま す。また、現在の束縛のことを 既存の最も局所的な束縛(most-local existing binding)と呼んで強調するこ ともあります。シンボルを通常に評価すると、かならずその現在の束縛の値を返します。

特殊形式 let および let* は、局所的な束縛を作成するためのものです。

Special Form: let (bindings…) forms…

この特殊形式は、bindingsに従って変数を束縛し、formsをすべて記 述された順序で評価します。let-形式はformsにある末尾の形式の 値を返します。

bindings はそれぞれ、(i)シンボルまたは(ii)形式のリスト (symbol value-form)になります。前者の場合シンボルはnil に束縛され、後者の場合symbolvalue-formの評価結果に束縛され ます。 value-formが省略された場合はnilが使用されます。

bindingsvalue-formはすべて現れた順に、どのシンボルが束縛されるよ り前に評価されます。例を示します。ZYの元の値、つ まり新規の値1ではなく2に束縛されます。

 
(setq Y 2)
     ⇒ 2
(let ((Y 1) 
      (Z Y))
  (list Y Z))
     ⇒ (1 2)
Special Form: let* (bindings…) forms…

この特殊形式はletのようなものですが、この場合、束縛するのはローカル 値を計算した直後で、次の変数のローカル値を計算する前になります。そのため、 bindingsの式は、このlet*形式で先行するシンボルを適切に参照できるよ うになります。下に示す例を、上のletの例と比べてみてください。

 
(setq Y 2)
     ⇒ 2
(let* ((Y 1)
       (Z Y))    ; Yの確立されたばかりの値を使う
  (list Y Z))
     ⇒ (1 1)

その他、局所的な束縛を作成する機能をすべて下に示します。

変数ではバッファローカルな束縛を持つことも可能です (see section バッファローカルな変数)。端末ローカルな束縛を持つ変数もわずかですがあ ります(see section 複数のディスプレイ)。このような種類の束縛処理は、通常の局所的な 束縛と多少類似していますが、それは、ローカライズした時点ではなく、Emacsの「どこ に」いるかということに依存します。

Variable: max-specpdl-size

この変数は、ローカル変数束縛とunwind-protectのクリーンアップの総数の 上限を定義します(see section 非局所脱出)。これらは、エラーが通知されるまで (データ"Variable binding depth exceeds max-specpdl-size"による)行なわ れます。

この制限を超えると、それに対応するエラーが発生します。これは、適切に定義され ていない関数への無限再帰を防ぐためのものです。

デフォルトの値は 600 です。

max-lisp-eval-depthはネスティングに対する別の制限です。 See section Eval


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.4 変数が"Void"である場合

シンボルにグローバル変数としての値をまったく指定していない場合、そのシンボルの グローバル値はvoidであるといいます。つまり、そのシンボルのセルの中にはLisp オブジェクトがないということです。そのシンボルを評価しようとすると、値を取得する ことはできず、void-variableエラーが発生します。

nilはvoidとは別であることに注意してください。シンボルnilはLisp オブジェクトで、ほかのオブジェクトと同じように変数の値にすることもできます。た だしこれはです。void変数には値はありません。

変数に値を代入した後で、makunboundを使用すると、変数は再度voidになります。

Function: makunbound symbol

この関数は、symbolの現在の束縛をvoidにします。続けて、このシンボルの値を 変数として使用しようとすると、再度セットするまでvoid-variableエラーに なります。

makunboundsymbolを返します。

 
(makunbound 'x)      ; xのグローバル
                     ;   値をvoidにする。
     ⇒ x
x
error--> Symbol's value as variable is void: x

symbolが局所的に束縛されている場合、makunboundは最も局所的な 束縛に作用します。シンボルの局所的な束縛をvoidにする方法はこれだけです。局所的 な束縛を作成する構文要素は、値とともに作成するためです。この場合、voidであ るという状態は、長くても束縛と同じ期間しか継続しません。構文要素を抜けるな どで束縛が削除された場合、直前の束縛またはグローバルな束縛が通常どおり再度現 れ、新規に現れた束縛がvoidである場合を除き、変数はvoidではなくなります。

 
(setq x 1)               ; グローバル束縛に値を設定
     ⇒ 1
(let ((x 2))             ; 局所的に束縛
  (makunbound 'x)        ; 局所的な束縛をvoidにする
  x)
error--> Symbol's value as variable is void: x
x                        ; グローバル束縛は変更されていない
     ⇒ 1

(let ((x 2))             ; 局所的に束縛
  (let ((x 3))           ; くり返し
    (makunbound 'x)      ; もっとも内側の局所的な束縛をvoidにする
    x))                  ; 参照: void
error--> Symbol's value as variable is void: x

(let ((x 2))
  (let ((x 3))
    (makunbound 'x))     ; 内側の束縛をvoidにしてから削除
  x)                     ; 外側のlet束縛が可視になる
     ⇒ 2

makunboundを用いてvoidにされた変数と、値が与えられなかったためにvoid だった変数は、区別できません。

boundp関数を使うと、変数が現在voidかどうかが調べられます。

Function: boundp variable

boundpは、variable(シンボル)がvoidでない場合(正確には、変数の 現在の束縛がvoidでない場合)はtを返します。voidである場合は、 nilを返します。

 
(boundp 'abracadabra)          ; voidから開始
     ⇒ nil
(let ((abracadabra 5))         ; 局所的に束縛
  (boundp 'abracadabra))
     ⇒ t
(boundp 'abracadabra)          ; グローバルではvoidのまま
     ⇒ nil
(setq abracadabra 5)           ; グローバルでも非voidにする
     ⇒ 5
(boundp 'abracadabra)
     ⇒ t

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.5 グローバル変数の定義

シンボルをグローバル変数として使用するということは、変数定義(variable definition)によってアナウンスできます。これは特殊形式で、defconstまた はdefvarのいずれかになります。

Emacs Lispでは、定義により3種類の目的が達成できます。まず、コードを読む人 間に対して、特定のシンボルが特定の方法で(変数として)使用されることを意図 されているということが伝わります。二番目に、このような情報がLispシステムに も伝わり、値や説明が与えられます。三番目に、etagsmake-docfile など、プログラム内の関数や変数のデータベースを作成するユーティリティにも情報 が伝わります。

defconstdefvarの主な違いは、プログラムが変数を変更するかどう かを人間の読者に対して伝えるという意図にあります。Emacs Lispは、変数を使用す る方法に関して、defconstで宣言したかdefvarで宣言したかという点 を区別しません。ただし、初期化には違いがあります。defconstは無条件で 変数を初期化しますが、defvarはvoidの場合にかぎり初期化を行ないます。

「ユーザ・オプションの変数は、プログラムによって値が変更されることがないので、 defconstで定義する方がいい」と思われることもありますが、プリロードされ ないライブラリで定義されている場合は、悪い結果を生じるでしょう。ライブラリが ロードされる際、defconstは前の値より優先します。ユーザの初期化ファイル を使用してユーザ・オプションをセットし、定義に対するデフォルト値を書き換えるこ ともできます。このため、ユーザ・オプションはdefvarで定義する必要があり ます。

Special Form: defvar symbol [value [doc-string]]

この特殊形式はsymbolをvalueに定義して、初期化します。この定義により、 symbolが変数として使用されており、したがってプログラムがセットや変更を行 うこともあるのだということが、コードを読む人にわかるようになります。これは、 ユーザ・オプション変数(プリロードされたEmacsの一部であるものを除く)にも使用 されます。symbolは評価されないので注意してください。定義されるシンボルは、 defvarに明示されている必要があります。

symbolに値がすでにある場合(すなわち、voidではない場合)、valueは 評価されず、symbolの値は変更されません。symbolがvoidでvalue が指定されている場合、defvarは評価を行ない、結果にsymbolをセット します。(valueを省略すると、symbolの値が変更されることはありません。)

Emacs LispモードでC-M-xを用いてトップレベルのdefvar形式を評価す る(eval-defun)と、eval-defunの特殊機能によりdefconstと 評価されます。この動作の目的は、個別の指示があった場合に、変数の値の再初期化 を確実に行なうということです。

symbolのカレント・バッファに、バッファローカルな束縛があれば、 defvarはローカルな値ではなくデフォルトの値をセットします。 See section バッファローカルな変数

doc-string引数があれば、これが変数の説明を指定します。(文書化を指定 できるということが、変数定義の主な利点のひとつです。)文書はシンボルの variable-documentation属性に格納されます。Emacsのヘルプ機能 (see section ヘルプ)はこの属性を検索します。

doc-stringの最初の文字が`*'の場合、この変数はユーザ・オプションで あると考えられます。これにより、ユーザがset-variableおよび edit-optionsで変数を簡単にセットすることができます。

たとえば、この形式はfooを定義しますが、その値は定義しません。

 
(defvar foo)
     ⇒ foo

次の例では、barの値を23にセットし、説明文字列も 指定します。

 
(defvar bar 23
  "The normal weight of a bar.")
     ⇒ bar

次の形式は、barの説明文字列を変更し、変数をユーザ・オプションにしますが、 値は変更しません。barにはすでに値があるためです。(追加されてい る(1+23)は実行されません。)

 
(defvar bar (1+ 23)
  "*The normal weight of a bar.")
     ⇒ bar
bar
     ⇒ 23

defvar特殊形式と等価である式の例を次に示します。

 
(defvar symbol value doc-string)
≡
(progn
  (if (not (boundp 'symbol))
      (setq symbol value))
  (put 'symbol 'variable-documentation 'doc-string)
  'symbol)

defvar形式はsymbolを返しますが、通常はファイル中のトップレベルで 使用されるので、値は問題になりません。

Special Form: defconst symbol [value [doc-string]]

この特殊形式はsymbolをvalueに定義して、初期化します。これにより、 symbolにグローバルな値があり、それがここで確立され、通常はプログラムの 実行によって変更されることも局所的に束縛されることもないということが、コー ドを読む人にわかるようになります。ただし、ユーザが変更することもできます。 symbolは評価されないので注意してください。定義されるシンボルは、 defconstに明示されている必要があります。

defconstは、かならずvalueを評価して、その結果にグローバルな値 symbolをセットします。これにはvalueが指定されているということが条件 です。symbolに、バッファローカルな束縛がカレント・バッファにある場合、 defconstはローカルな値ではなくデフォルトの値をセットします。

注意: 標準的なプリロードを行なっていないライブラリでは、 defconstをユーザ・オプション変数に使用しないでください。このような変数 にはファイル`.emacs'でユーザによる値の指定ができるようにしておき、ライブ ラリを後からロードしても効果があるようにしてください。

ここで、piはおそらく誰にも変更されない(インディアナ州議会はやろうとし たが)定数です。ただし、二番目の形式でそれは単に勧告であるということを示してい ます。

 
(defconst pi 3.1415 "Pi to five places.")
     ⇒ pi
(setq pi 3)
     ⇒ pi
pi
     ⇒ 3
Function: user-variable-p variable

variableがユーザ・オプション(ユーザによるカスタマイズのためのセットを意 図した変数)であれば、この関数はtを返し、それ以外の場合はnilを 返します。(ユーザ・オプション以外の変数も、Lispプログラム内で内部的に使用され ており、ユーザはそれを知る必要はありません。)

ユーザ・オプション変数は、variable-documentation属性の最初の文字によっ て、ほかの変数から区別されています。`*'で始まる文字列の属性があれば、その 変数はユーザ・オプションです。

ユーザ・オプションの変数にvariable-interactive属性があれば、 set-variableコマンドはその値を使用して、変数に対する新しい値の読込み を制御します。属性の値は、interactiveの場合と同じように使用されます。

警告!: 変数に局所的な束縛がある場合に、特殊形式defconst およ びdefvarを使用すると、局所的な束縛の値がセットされます。グローバルな束 縛は変更されません。これは本来意図したところではないので、これを防ぐために、 このような特殊形式はファイルのトップレベルで使用してください。ここでは、通常 局所的な束縛は有効ではないからです。また、かならずファイルをロードしてから 変数に対する局所的な束縛を作成するようにしてください。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.6 変数の値へのアクセス

変数を参照する場合、最も一般的な方法は、その変数を特定するシンボルを書くことです (see section シンボル形式)。この場合、プログラムを記述する際に変数名を指定する必 要があります。これが通常のやり方でしょう。ただし、どの変数を参照するかを実行 中に選択しなければならないこともあります。これには、symbol-valueを使用 します。

Function: symbol-value symbol

この関数は、symbolの値を返します。これは、シンボルの局所的な束縛の中 で最も内側にあるものの値です。また、局所的な束縛がない場合はグローバルな値 になります。

 
(setq abracadabra 5)
     ⇒ 5
(setq foo 9)
     ⇒ 9

;; ここでは、シンボルabracadabra
;;   の値が調べられる。
(let ((abracadabra 'foo))
  (symbol-value 'abracadabra))
     ⇒ foo

;; ここでは、abracadabra
;;   すなわちfooの
;;   値が調べられる
(let ((abracadabra 'foo))
  (symbol-value abracadabra))
     ⇒ 9

(symbol-value 'abracadabra)
     ⇒ 5

エラーvoid-variableは、symbolに局所的な束縛もグローバルな値も ないということを示します。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.7 変数の値を変更する方法

変数の値を変更する場合、通常は特殊形式setqを使用します。実行時に変数の 選択を計算する必要がある場合は、関数setを使用します。

Special Form: setq [symbol form]…

この特殊形式は、変数の値を変更するための最も一般的な方法です。symbolに は、それぞれ対応するformの評価結果である新規の値が与えられます。シンボルに 対して存在する束縛の中で、最もローカルなものが変更されます。

setqsymbolを評価せず、記述したシンボルをセットします。この引数は 自動的にquote(automatically quoted)されます。 `q'は「quoted」の略 です。

setq形式の値は、最後にあるformの値になります。

 
(setq x (1+ 2))
     ⇒ 3
x                   ; xがグローバルな値を取得
     ⇒ 3
(let ((x 5)) 
  (setq x 6)        ; xの局所的な束縛をセット
  x)
     ⇒ 6
x                   ; グローバルな値は変更されない
     ⇒ 3

最初のformが評価され、それから最初のsymbolがセットされ、次に二番目の formが評価され、二番目のsymbolがセットされるという順序で実行されるこ とに注意してください。

 
(setq x 10          ; xyの値を計算する前に
      y (1+ x))     ;   セットされているということに注意
     ⇒ 11             
Function: set symbol value

この関数は、symbolの値をvalueにセットし、valueを返します。 setは関数なので、symbolに対して記述した式が評価されて、セットする シンボルを取得します。

変数の既存の束縛で最も局所的な束縛がセットされます。隠されている束縛は影響さ れません。

 
(set one 1)
error--> Symbol's value as variable is void: one
(set 'one 1)
     ⇒ 1
(set 'two 'one)
     ⇒ one
(set two 2)         ; twoはシンボルoneに評価される
     ⇒ 2
one                 ; したがって、oneがセットされる
     ⇒ 2
(let ((one 1))      ; oneのこの束縛をセット
  (set 'one 3)      ;   グローバルな値ではない
  one)
     ⇒ 3
one
     ⇒ 2

symbolが実際のシンボルではない場合、wrong-type-argument エラーが発生します。

 
(set '(x y) 'z)
error--> Wrong type argument: symbolp, (x y)

論理的にいうと、setsetqよりもプリミティブなものです。 setqを使っている箇所はすべて、setを使うように書き換えることが簡 単にできます。 setが使える場合は、setqをマクロで定義することも できます。ただし、set自身が使用されることはほとんどなく、初心者が知る 必要はないでしょう。この操作が便利になるのは、実行時にどの変数をセットするかを 選択するような場合に限られます。たとえば、set-variableコマンドはユーザ から変数名を読込んで変数をセットするので、setを使用する必要があります。

Common Lisp注意書き: Common Lispでは、setはかならずシンボルの特殊な値を変更し、 辞書的な束縛をすべて無視します。Emacs Lispでは、変数と束縛はすべて(実質上)特 殊なので、setは常に、最も局所的な束縛に影響を与えます。

変数をセットする別の関数には、リストに入っていない要素をリストに追加するように 設計されているものがあります。

Function: add-to-list symbol element

この関数は、elementがその値のメンバになっていない場合に、element を従来の値にconsすることで、変数symbolの値をセットします。この関数は、 リストが更新されてもされなくてもリストを返します。symbolの値は、呼び 出す前にリストにしておくといいでしょう。

symbolを引用符で囲むということは含意されていません。add-to-listsetと同じような通常の関数で、setqとは異なっています。必要 に応じて、引数を引用符で囲んでください。

add-to-listの使用例を示します。

 
(setq foo '(a b))
     ⇒ (a b)

(add-to-list 'foo 'c)     ;; cを追加
     ⇒ (c a b)

(add-to-list 'foo 'b)     ;; 効果なし
     ⇒ (c a b)

foo                       ;; fooを変更
     ⇒ (c a b)

(add-to-list 'var value)は、次のような式に相当 します。

 
(or (member value var)
    (setq var (cons value var)))

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.8 変数束縛のスコーピングに関する規則

指定されたシンボルfooには、Lispプログラムの各所で確立されたいくつかのロー カルな変数束縛と、グローバルな束縛が存在することがあります。最も新しく確立さ れた束縛は、ほかのものより優先されます。

Emacs Lispの局所的な束縛には無限スコープ(indefinite scope)動的エクステント(dynamic extent)があります。 スコープ(Scope)とは、束縛が ソースコードの本文中のどの部分にアクセスできるかということを表していま す。無限スコープとは、変数の束縛がプログラムのどの部分にもアクセスできる可能性が あるということです。エクステント(Extent)は、プログラムの実行中、 どの時点で束縛が存在するかということを表します。動的エクステントとは、その 束縛を確立した構文要素がアクティブである間は、かならず束縛が継続して存在する ということを表します。

動的エクステントと無限スコープを組み合わせを動的スコーピング(dynamic scoping)といいま す。それに対して、ほとんどのプログラミング言語では、 辞書的スコーピング(lexical scoping)を用いています。この場合、ローカル変数への参 照は、関数の内部か、またはその変数を束縛するブロックの内部に存在している必要 があります。

Common Lispに関する注意: Common Lispで"special"として宣言された変数は、 Emacs Lispの変数と同じようにスコープは動的になります。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.8.1 スコープ

Emacs Lispは、ローカルな変数束縛に無限スコープ(indefinite scope)を使用しま す。これは、プログラムのテキスト内にある関数はすべて、指定された変数束縛への アクセスが可能であることを表します。次のような関数定義を考えてみてください。

 
(defun binder (x)   ; xbinderで束縛される
   (foo 5))         ; fooはほかの関数

(defun user ()      ; xuserで使用される
  (list x))

辞書的にスコープしている言語では、binderの中のxuserでアクセスすることはできません。userが関数binder に含まれていないためです。ただし、動的スコープのEmacs Lispでは、userは、 binderで確立されるxの束縛を参照する場合と参照しない場合(状 況による)があります。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.8.2 エクステント

エクステント(extent)とは、プログラムを実行する際に変数名が有効である時間 を表します。 Emacs Lispでは、変数が有効になるのは、その変数を束縛する形式の実 行中に限られます。これを、動的エクステント(dynamic extent)といいます。Cや Pascalなど、ほとんどの言語では「ローカル」または「自動」変数に動的エクステントが あります。

別の動的エクステントに無限エクステント(indefinite extent)があります。これは、変 数の束縛が、その束縛を作成した形式から抜けた後も存続するという意味です。たと えば、Common Lisp やSchemeではこれをサポートしていますが、Emacs Lispではサポー トしていません。

これを図示すると、以下の関数でmake-addは、独自の引数mnを追加する関数を返します。これはCommon Lispでは動作しますが、Emacs Lispを意図した場合には動作しません。make-addへの呼出しを抜けた 後、変数nは実引数2には束縛されていないためです。

 
(defun make-add (n)
    (function (lambda (m) (+ n m))))  ; 関数を返す
     ⇒ make-add
(fset 'add2 (make-add 2))  ; (make-add 2)で
                           ;   関数add2を定義
     ⇒ (lambda (m) (+ n m))
(add2 4)                   ; 2と4を加算
error--> Symbol's value as variable is void: n

Lispの方言には"closure"を持つものがあります。これは関数と同じようなオブ ジェクトですが、変数束縛も記録できます。Emacs Lispにはclosureはありま せん。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.8.3 動的スコープの実装

簡単な実装例(Emacs Lispの実際の動作というわけではありません)により、動的束縛 が理解しやすくなるでしょう。この手法は深い束縛(deep binding)と呼ばれて おり、初期の Lispシステムで使用されていました。

束縛のスタック(変数と値の対)があるとします。関数またはlet形式の開 始地点で、引数またはそこで作成されるローカル変数に対して束縛をスタックにプッ シュします。束縛構造を抜けた時点で、スタックから束縛をポップすることができます。

変数の値を検索する場合は、スタックを先頭から末尾まで調べて、その値に対する束 縛を検索します。その束縛からの値は変数の値です。変数をセットするには、まず現在の 束縛を検索し、新規の値をその束縛に格納します。

みればわかるはずですが、関数の束縛は実行中であればかならず有効になっています。 これは、ほかの関数を呼出している場合でも変わりません。束縛のエクステントが動的 であるというのは、このためです。ほかの関数からでも、その束縛が有効である時点で 同じ変数を使用すると、この束縛を参照できます。スコープが無限であるというのは、こ のためです。

変数スコーピングを実装する場合、GNU Emacs Lispでは浅い束縛(shallow binding)とい う手法を使用します。各変数には標準的な位置があり、現在の値はすべてその場所に あります。すなわち、シンボルの値セルです。

浅い束縛では、変数のセットは、値を値セルに格納することで行ないます。新規の束縛は、 従来の値(直前の束縛に属する)をスタックにプッシュし、ローカルな値を値セルに 格納することで作成されます。束縛の除去は、従来の値をスタックから値セルにポッ プすることで行ないます。

浅い束縛を使用する理由は、深い束縛と同じ結果が得られて高速に実行できるためで す。これは、束縛を検索する必要がないためです。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.8.4 動的スコーピングの標準的な使い方

ある関数で変数を束縛してほかの関数で使用することは、高度な手法ですが、乱用する とプログラムが判読しにくくなります。この手法をわかりやすく使用する方法は二とおり あります。

どちらの場合でも、変数はdefvarで定義してください。これにより、ほかのプ ログラマたちも、関数間の使用方法をみればプログラムがわかるようになります。ま た、バイトコンパイラからの警告も避けられます。変数名が重複しないように注意し てください--xなどの短い名称は避けてください。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.9 バッファローカルな変数

グローバル変数とローカル変数の束縛は、ほとんどのプログラミング言語でさまざま な形式で行なわれています。Emacsもあまり一般的でない種類の変数束縛をサポートして います。これはバッファローカル(buffer-local)な束縛といい、適用される バッファはひとつに限定されます。Emacs Lispは編集コマンドをプログラムするため のものであり、別々のバッファに別々の値が入るということが、カスタマイズの重要 な方法なのです。 (指定されたX端末に対して局所的な束縛を持つ変数も多少あります。 複数のディスプレイを参照してください。)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.9.1 バッファローカルな変数(紹介)

バッファローカルな変数には、バッファローカルな束縛があり、特定のバッファ に対応しています。束縛は、そのバッファがカレント・バッファであれば有効です。 カレント・バッファでなければ有効ではありません。バッファローカルな束縛が有 効な時点で変数をセットすると、新規の値はその束縛にはいるので、グローバルな束 縛は変更されません。すなわち、変更がわかるのはそのバッファのみになるというこ とです。

変数には、バッファローカルな束縛が一部のバッファにしかないというものがあり ます。グローバルな束縛は、独自の束縛を持たないバッファすべてに共有されます。 したがって、バッファローカルな束縛を持たないバッファにある変数をセットすると、 新規の値は、バッファローカルな束縛を持たないバッファすべてで可視になります。 (ここでは、問題を複雑にするlet形式の局所的な束縛がないことを前提にして います。)

バッファローカルな束縛を使用する場合、もっとも一般的なものは主モードのもの で、コマンドの動作を制御する変数を変更するという方法です。たとえば、Cモードと Lispモードは、どちらも変数paragraph-startをセットすることにより、空行の みがパラグラフを分割するという指定を行ないます。これは、CモードやLispモードに なったバッファローカルな変数を作成し、そのモードに対する新規の値をセットする ことで行ないます。

バッファローカルな束縛は、通常、主モード・コマンドが使用する make-local-variableで作成します。これはカレント・バッファのみに影響し ます。ほかのバッファ(まだ作成されていないものも含む)はグローバルな値の共有を継 続します。

さらに高度な操作として、make-variable-buffer-localを呼出して変数を automatically buffer-localと印づけるという方法があります。これは、す べてのバッファで変数をローカルにすると考えることができます。さらに正確にいう と、この効果は、変数をセットした場合、その変数がカレント・バッファに対してバッ ファローカルでなければ、自動的にバッファローカルになるということです。す べてのバッファは、最初は変数のグローバルな値を共有しますが、setqがあれ ば、カレント・バッファに対するバッファローカルな束縛を作成します。新規の値 はバッファローカルな束縛を格納し、(デフォルトの)グローバルな束縛は変更され ません。グローバルな値はsetqで変更されることがなくなるので、変更には setq-defaultを使用する必要があります。

警告!: 変数が一つまたは複数のバッファにローカル変数を持っていた場合、 変数をletに束縛し、ほかの束縛が有効であるほかのカレント・バッファに変更し てletを抜けると、Emacsはひじょうに混乱します。これにより、グローバル束 縛および局所的な束縛の値がごちゃまぜになります。

正常を保つためには、このような一連の動作を避けるようにしてください。カレント ・バッファを変更する各コードの周囲でsave-excursionを使用すると、このよ うな問題が発生します。何を避けるかという例をここに示します。

 
(setq foo 'b)
(set-buffer "a")
(make-local-variable 'foo)
(setq foo 'a)
(let ((foo 'temp))
  (set-buffer "b")
  body…)
foo ⇒ 'a      ; バッファ`a'に対する従来のローカルな値が
               ;   デフォルト値になる
(set-buffer "a")
foo ⇒ 'temp   ; 消去すべきローカルな値が
               ;   バッファ`a'に対するローカルな値になる

ただし、ここで示すようにsave-excursionが問題を回避します。

 
(let ((foo 'temp))
  (save-excursion
    (set-buffer "b")
    body…))

body内のfooへの参照が、バッファ`b'に対してバッファローカルな束 縛にアクセスするという点に注意してください。

ファイルがローカルな値を指定する場合、これらの値はファイルをvisitした時点でバッ ファローカルになります。 See section 主モードの自動選択の仕組み


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.9.2 バッファローカルな束縛の作成と削除

Command: make-local-variable variable

この関数は、バッファローカルな束縛を、variable(シンボル)に対する カレント・バッファに作成します。ほかのバッファには影響しません。返される値は variableになります。

バッファローカルなvariableの値は、最初は、variableの直 前の値と同じになります。variableがvoidの場合はvoidのままです。

 
;; In buffer `b1':
(setq foo 5)                ; 全バッファに影響
     ⇒ 5
(make-local-variable 'foo)  ; `b1'内でローカルになる
     ⇒ foo
foo                         ; 値を変更
     ⇒ 5                   ;   しなかった
(setq foo 6)                ; `b1'の値を
     ⇒ 6                   ;   変更
foo
     ⇒ 6

;; バッファ`b2'では、値は変更されていません。
(save-excursion
  (set-buffer "b2")
  foo)
     ⇒ 5

バッファローカルな変数を作成する場合、その変数に対するlet束縛 の内部で行なっても動作しません。これは、letが異なる種類の束縛を区別しな いためです。束縛がどの変数に対して作成されたかということのみを識別します。

変数が端末ローカルであれば、この関数はエラーを通知します。このような 変数には、バッファローカルな束縛も使用できません。 See section 複数のディスプレイ

注意!: フック変数にはmake-local-variableではなく make-local-hookを使用してください。 See section フック

Command: make-variable-buffer-local variable

この関数はvariable(シンボル)が自動的にバッファローカルになるように印づけるの で、その後でセットしようとすると、その時点のカレント・バッファに対してローカル になります。

戻り値はvariableです。

Function: local-variable-p variable &optional buffer

バッファbuffer(デフォルトではカレント・バッファ)の中でvariable が バッファローカルであれば、tを返します。それ以外の場合は nilを 返します。

Function: buffer-local-variables &optional buffer

この関数は、bufferの中の、バッファローカルな変数の連想リストを返しま す。これは、連想リスト(see section 連想リスト)を返します。このリストでは、 それぞれ対応する項目にバッファローカルな変数がひとつと、その値が含まれてい ます。bufferの中の、バッファローカルな変数がvoidであれば、結果のリス トに直接はいります。bufferが省略されると、カレント・バッファが使用され ます。

 
(make-local-variable 'foobar)
(makunbound 'foobar)
(make-local-variable 'bind-me)
(setq bind-me 69)
(setq lcl (buffer-local-variables))
    ;; 最初は、すべてのバッファでローカルな組込み変数
⇒ ((mark-active . nil)
    (buffer-undo-list nil)
    (mode-name . "Fundamental")
    …
    ;; 次に、非組込みのローカル変数
    ;; これはローカルでvoidになっている
    foobar
    ;; これはローカルでvoidになっていない
    (bind-me . 69))

このリストにあるコンス・セルのCDRに新規の値を格納しても、変数のローカル な値は変更されないので注意してください。

Command: kill-local-variable variable

この関数は、カレント・バッファにあるvariable(シンボル)に関して、 バッファローカルな束縛があればそれを消去します。結果として、variableのグローバ ル(デフォルト)束縛がこのバッファで可視になります。通常、このために variableの値が変更されます。これは、グローバルな値が、削除されたばかり の、バッファローカルな値と通常は違うためです。

セットされると自動的にローカルになる変数の局所的な束縛をkillすると、グローバ ルな値はカレント・バッファで可視になります。ただし、変数を再度セットすると、 局所的な束縛が再度作成されます。

kill-local-variablevariableを返します。

この関数はコマンドです。これは、バッファローカルな変数の対話的な作成が便利 であり、同様にバッファローカルな変数を対話的にkillすることも便利であるためです。

Function: kill-all-local-variables

この関数は、カレント・バッファに関するバッファローカルな変数の束縛をすべて 除去しますが、 "permanent"と印づけられた変数は例外です。結果として、バッファ にはほとんどの変数に対するデフォルトの値がみえるようになります。

また、この関数はバッファに対するほかの特定情報のリセットも行ないます。これは、ロー カルなキーマップをnilに、構文テーブルをstandard-syntax-tableの 値に、略称テーブルをfundamental-mode-abbrev-tableの値にセットします。

主モードのコマンドはすべて、この関数の呼出しで開始されます。これには、 Fundamentalモードに切り替え、直前の主モードの効力をほとんど削除するという効力 があります。この作業を確実に行なうには、主モードがセットする変数に permanentの印 をつけないようにしてください。

kill-all-local-variablesnilを返します。

ローカル変数は、その変数名(シンボル)のpermanent-localの属性が nilで ない場合にpermanentになります。常時ローカルな変数は、そのファイル の内容の編集方法ではなく、ファイルの場所や保存方法に属しているデータに対して 適切なものになります。


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

10.9.3 バッファローカルな変数のデフォルト値

バッファローカルな束縛のある変数のグローバルな値はデフォルト(default) 値ともいいます。この値は、明示して無視しないかぎり有効になるためです。

関数default-valuesetq-defaultは、変数のデフォルト値をアクセス したり変更したりします。これには、カレント・バッファにバッファローカル な束縛があるかどうかは無関係です。たとえば、setq-defaultを用いて、ほと んどのバッファに対するparagraph-startのデフォルトセットを変更することが できます。これは、CまたはLispモードのバッファにいて、この変数に対して バッファローカルな値がある場合でも同様です。

また、特殊形式defvarおよびdefconstは、ローカル値ではなくデフォ ルト値(変数をセットすることがあれば)のセットも行ないます。

Function: default-value symbol

この関数は、symbolのデフォルト値を返します。この値は、この変数に関して 独自の値を持たないバッファにある値です。symbolがバッファローカルでな ければ、この値はsymbol-valueと等価になります (see section 変数の値へのアクセス)。

Function: default-boundp symbol

関数default-boundpは、symbolのデフォルト値がvoid以外である かどうかを示します。(default-boundp 'foo)nilを返す場合、 (default-value 'foo)はエラーになります。

boundpsymbol-valueに対応することと同じように、 default-boundpdefault-valueに対応します。

Special Form: setq-default symbol value

これは、symbolのデフォルト値をvalueにセットします。 symbolの評価は行ないませんが、valueは評価します。 setq-default形式の値はvalueになります。

symbolがカレント・バッファに対してバッファローカルでなく、自動的にバッ ファローカルになるという印もついていない場合、setq-defaultの効果 は setqと同じになります。symbolがカレント・バッファに対してバッ ファローカルであれば、これにより、ほかのバッファの値は変更されますが(バッファ ローカルな値を持たないバッファの場合)、カレント・バッファが認識する値は変更 されません。

 
;; バッファ`foo'で:
(make-local-variable 'local)
     ⇒ local
(setq local 'value-in-foo)
     ⇒ value-in-foo
(setq-default local 'new-default)
     ⇒ new-default
local
     ⇒ value-in-foo
(default-value 'local)
     ⇒ new-default

;; `bar'バッファ(新規)で:
local
     ⇒ new-default
(default-value 'local)
     ⇒ new-default
(setq local 'another-default)
     ⇒ another-default
(default-value 'local)
     ⇒ another-default

;; `foo'バッファに戻る:
local
     ⇒ value-in-foo
(default-value 'local)
     ⇒ another-default
Function: set-default symbol value

この関数はsetq-defaultと同じようなものですが、symbolが評価さ れるという点が異なります。

 
(set-default (car '(a b c)) 23)
     ⇒ 23
(default-value 'a)
     ⇒ 23

[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Yasutaka SHINDOH on September, 29 2006 using texi2html 1.76.