[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
変数(variable)は、プログラムにおいて値を表すために使う名前です。 ほとんどすべてのプログラム言語には、ある種の変数があります。 Lispプログラムのテキストでは、シンボルの構文を使って変数を書きます。
ほとんどのプログラム言語と違って、Lispでは、 プログラムはLispオブジェクトで表現し、テキスト表現は副次的なものです。 変数として使うLispオブジェクトはシンボルです。 シンボル名が変数名であり、変数の値はシンボルの値セルに格納されています。 変数としてのシンボルの使い方は、関数名としての使い方とは独立しています。 See 節 7.1 シンボルの構成要素。
Lispプログラムを構成するLispオブジェクト群は、 プログラムのテキスト表現を決定します。 つまり、Lispオブジェクト群に対する単なる入力構文です。 これは、たとえば、Lispプログラムのテキスト表現では、 変数を表現するシンボルの入力構文で変数を書く理由です。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
変数を使うもっとも簡単な方法は、 グローバルに(globally、大局的に)使うことです。 つまり、どんなときにも変数にはたった1つの値だけがあり、 (少なくともここでは)Lispシステム全体にその値が有効になります。 新たな値を設定するまで、その値が有効であり続けます。 新たな値で古い値を置き換えると、変数には古い値の痕跡はなにも残りません。
シンボルの値はsetq
で指定します。 たとえば、
(setq x '(a b)) |
は、変数x
に値(a b)
を与えます。 setq
は、最初の引数、つまり、変数の名前を評価せず、 新しい値である第2引数を評価することに注意してください。
変数にいったん値を与えれば、 式としてシンボルそのものを使うことによりその値を参照できます。 つまり、つぎのとおりです。
x => (a b) |
ただし、上に示したフォームsetq
を実行してあると仮定します。
同じ変数に値を設定し直すと、 新しい値で古い値を置き換えます。
x => (a b) (setq x 4) => 4 x => 4 |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
Emacs Lispには、通常それ自身に評価されるある種のシンボルがあります。 `:'で始まる名前の任意の変数、および、nil
とt
です。 これらのシンボルを再束縛することはできず、 それらの値を変更することもできません。 nil
やt
を設定しようとしたり束縛しようとすると、 エラーsetting-constant
を通知します。 `:'で始まる名前のシンボルに関してもそうですが、 そのようなシンボルにそれ自身を設定することはできます。
nil == 'nil => nil (setq nil 500) error--> Attempt to set constant symbol: nil |
nil
であると、 `:'で始まる名前の変数を望みの値に設定したり束縛したりできる。 これは、そのようなことを行う古いLispプログラムの実行を可能にするためである。[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
グローバル変数は、 明示的に新しい値で置き換えない限り存続する値を持ちます。 一時的にしか存在しない変数値、 つまり、プログラムのある部分を完了するまでのみ存在する変数値を 作れると便利なことがあります。 このような値をローカル(local、局所的)と呼び、 そのように使われる変数をローカル変数(local variables)と呼びます。
たとえば、関数を呼び出したとき、その引数変数は、 関数を抜けるまで存続する新たなローカルな値を受け取ります。 スペシャルフォームlet
は、指定した変数の新たなローカル値を 明示的に確立します。 これらはフォームlet
を抜けるまで存続します。
ローカル値を確立すると、 変数の以前の値(あるいは値がないこと)を保存します。 ローカル値の存続期間が終了すると、以前の値を復元します。 この期間は、以前の値を隠して(shadowed)いて 以前の値は見えません。 グローバル値でもローカル値でも隠せます(see 節 10.9.1 スコープ)。
変数がローカルなときに(setq
などで)その変数を設定すると、 ローカル値を置き換えます。 隠されているグローバル値や以前のローカル値を変更しません。 このふるまいをモデル化するために、 変数のローカル値に加えて変数のローカル束縛(local binding)を 考えます。
ローカル束縛とは、ローカル値を保持する概念的な場所です。 関数やlet
などのスペシャルフォームに入るたびに ローカル束縛を作成します。 関数やフォームlet
から抜けるとローカル束縛を削除します。 ローカル束縛が存続する限り、変数の値はそこに保持されています。 ローカル束縛が存在するときにsetq
やset
を使うと、 ローカル束縛の中に別の値を格納します。 新たな束縛を作るのではありません。
グローバル値を保持する概念的な場所を グローバル束縛(global binding)ともいいます。
変数には一度に複数のローカル束縛がありえます (たとえば、同じ変数を束縛する入れ子になったフォームlet
があるとき)。 そのような場合、既存のもっとも最近に作成されたローカル束縛が、 変数の現在の束縛(current binding)です。 (この規則を動的スコープ(dynamic scoping)と呼びます。 see 節 10.9 変数束縛のスコープルール) ローカル束縛がまったくなければ、変数のグローバル束縛が現在の束縛です。 現在の束縛のことを強調して既存の最ローカル束縛と呼ぶこともあります。 シンボルの通常の評価では、その現在の束縛の値を返します。
スペシャルフォームlet
やlet*
は、 ローカル束縛を作るためにあります。
let
フォームは、formsの最後のフォームの値を返す。
bindingsのおのおのは、(i)シンボルであるか、 (ii)フォーム(symbol value-form)
のリストである。 前者は、シンボルにnil
を束縛する。 後者は、symbolにvalue-formの評価結果を束縛する。 value-formを省略するとnil
を使う。
bindingsのvalue-form群すべてを現れる順に評価してから、 シンボルにそれらの値を束縛する。 例をつぎに示す。 Z
は、Y
の古い値2に束縛され、Y
の新しい値1ではない。
(setq Y 2) => 2 (let ((Y 1) (Z Y)) (list Y Z)) => (1 2) |
let
に似ているが、 変数のローカル値を計算し終えた直後にその変数を束縛し、 つぎの変数のローカル値の計算に進む。 したがって、bindings内の式では、このlet*
フォーム内で まえにあるシンボルを参照できる。 つぎの例を上のlet
の例と比較してほしい。
(setq Y 2)
=> 2
(let* ((Y 1)
(Z Y)) ; 設定し終えたばかりの
|
以下にローカル束縛を作成するその他の機能の完全な一覧をあげておきます。
変数は、バッファローカルな束縛(see 節 10.10 バッファローカルな変数や フレームローカルな束縛(see 節 10.11 フレームローカルな変数)を持つことができます。 少数の変数は、端末にローカルな束縛(see 節 28.2 複数ディスプレイ) を持つこともできます。 この種の束縛は普通のローカル束縛と同じように働きますが、 これらはEmacsの『どの部分』にいるかに依存したローカル化であり、 時間的なローカル化ではありません。
"Variable binding depth exceeds max-specpdl-size"
を 伴った)エラーを通知するまでに許される、 ローカル変数束縛と unwind-protect
による後始末(see 節 9.5 非ローカル脱出)の 全体の個数の制限を定義する。
この制限、および、これを超えたときのエラーは、 不正に定義された関数によってLispが無限に再帰することを防止する 1つの方法である。
デフォルト値は600である。 Lispデバッガに入ったとき、 制限に近い場合にはデバッガ自身が実行できることを保証するために値を増やす。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
シンボルにグローバル変数としての値を一度も与えていないとき、 そのシンボルのグローバル値は空(void)であるといいます。 いいかえれば、シンボルの値セルにはどんなLispオブジェクトも入っていません。 シンボルを評価しようとすると、値ではなくエラーvoid-variable
を得ます。
nil
という値は空とは異なることに注意してください。 シンボルnil
はLispオブジェクトであり、他のオブジェクトと同様に 変数の値になりえます。 それは値なのです。 空な変数はいかなる値も持たないのです。
変数に値を与えたあとでは、makunbound
を使って 再度その変数を空にできます。
void-variable
を通知する。
makunbound
はsymbolを返す。
(makunbound 'x) ; 変数
|
symbolがローカルに束縛されていると、 makunbound
は既存の最ローカル束縛に作用する。 ローカル束縛を作成するすべての構文は変数に値を与えるため、 これはシンボルのローカル束縛を空にする唯一の方法である。 この場面では、空の状態は、束縛が存在する限り存続する。 束縛を作成した構造から抜け出して束縛が削除されると、 通常どおりそれ以前のローカル束縛かグローバル束縛が有効になり、 その束縛が空でなければ変数は空ではない。
(setq x 1) ; グローバル束縛に値を入れる
=> 1
(let ((x 2)) ; ローカルに束縛する
(makunbound 'x) ; ローカル束縛を空にする
x)
error--> Symbol's value as variable is void: x
x ; グローバル束縛は変更されていない
=> 1
(let ((x 2)) ; ローカルに束縛する
(let ((x 3)) ; もう一度
(makunbound 'x) ; もっとも内側のローカル束縛を空にする
x)) ; 参照するが、それは空
error--> Symbol's value as variable is void: x
(let ((x 2))
(let ((x 3))
(makunbound 'x)) ; 内側の束縛を空にし、それを削除する
x) ; 外側の
|
makunbound
で空にした変数は、 一度も値を受け取ったことがなく、そのために空である変数と区別できません。
変数が現在、空であるかどうかは関数boundp
を使って調べられます。
boundp
は、(シンボル)variableが空でなければ、 より正確にいえば、現在の束縛が空でなければt
を返す。 さもなければnil
を返す。
(boundp 'abracadabra) ; 空で始める => nil (let ((abracadabra 5)) ; ローカルに束縛する (boundp 'abracadabra)) => t (boundp 'abracadabra) ; グローバルにはまだ空である => nil (setq abracadabra 5) ; グローバルに空でなくする => 5 (boundp 'abracadabra) => t |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
スペシャルフォームdefconst
やdefvar
の変数定義を使って、 シンボルをグローバル変数として使う意図を表明できます。
Emacs Lispでは、定義には3つの目的があります。 まず、コードを読む人向けに、特定のシンボルを(変数として)特定目的に 使う意図があることを知らせます。 第2に、Lispシステムに対しては、値と説明文字列を提供して これらのことを伝えます。 第3に、プログラム内の関数や変数のデータベースを作成する etags
やmake-docfile
などのユーティリティに情報を提供します。
defconst
とdefvar
の違いは、主に好みの問題であり、 値が変更されるかどうかを人に伝えます。 Emacs Lispは、defconst
やdefvar
の宣言に基づいて 変数の使い方を制限することはしません。 しかしながら、初期化に関しては違いがあります。 defconst
は無条件に変数を初期化しますが、 defvar
は変数が空である場合にのみ初期化します。
defvar
に明示的に現れる必要がある。
symbolの値が空でありvalueを指定してあると、 defvar
はvalueを評価し、その結果をsymbolに設定する。 しかし、symbolにすでに値があれば(つまり、空でなければ)、 valueをまったく評価せず、symbolの値も変更しない。 valueを省略した場合、symbolの値をいっさい変更しない。
symbolにカレントバッファでバッファローカルな束縛がある場合には、 defvar
はデフォルト値に作用する。 それは、バッファには独立であり、現在の(バッファローカルな)束縛ではない。 defvar
は、デフォルト値が空の場合にデフォルト値を設定する。 see 節 10.10 バッファローカルな変数。
emacs-lispモードにおいてC-M-x(eval-defun
)でトップレベルの フォームdefvar
を評価すると、 eval-defun
の特別な機能により、 変数の値が空かどうかを調べずに無条件に変数に設定する。
doc-stringがあれば、それは変数の説明文を指定する。 (説明文を指定できるのは、変数定義の主な利点の1つである。) 説明文はシンボルの属性variable-documentation
に格納する。 Emacsのヘルプ関数(see 節 23. 説明文)は、この属性を調べる。
doc-stringの最初の文字が`*'であると、 この変数をユーザーオプションと考えることを意味する。 これにより、ユーザーはコマンドset-variable
やedit-options
を 使って簡単に変数を設定できる。 しかしながら、ユーザーオプションの変数には、 defvar
ではなくdefcustom
を使ったほうがよく、 そうすればカスタマイズ情報を指定できる。 see 節 13. カスタマイズ定義の書き方。
いくつか例をあげる。 つぎのフォームはfoo
を定義するが初期化はしない。
(defvar foo) => foo |
つぎの例は、bar
の値を23
に初期化し、説明文字列を与える。
(defvar bar 23 "The normal weight of a bar.") => bar |
つぎの例は、bar
の説明文字列を変更し、 この変数をユーザーオプションにする。 しかし、bar
にはすでに値が設定してあるので、 その値は変更しない。 (さらに(1+ nil)
は評価するとエラーになるが、 評価されないのでエラーはない。)
(defvar bar (1+ nil) "*The normal weight of a bar.") => bar bar => 23 |
つぎの例は、スペシャルフォームdefvar
に等価な式である。
(defvar symbol value doc-string) == (progn (if (not (boundp 'symbol)) (setq symbol value)) (if 'doc-string (put 'symbol 'variable-documentation 'doc-string)) 'symbol) |
フォームdefvar
はsymbolを返すが、 通常このフォームはファイルのトップレベルで使われ、そこでは値は関係ない。
defconst
に明示的に現れる必要がある。
defconst
は、valueがあればつねにvalueを評価し、 その結果をsymbolに設定する。 symbolにカレントバッファのバッファローカルな束縛がある場合には、 defconst
はデフォルト値を設定し、 バッファローカルな値にではない。 (しかし、defconst
で定義するシンボルには、 バッファローカルな束縛を作るべきではない。)
つぎの例では、pi
は、(インディアナ州立法府はいうにおよばず) だれも変更すべきではないと考えられる定数である。 しかし、2番目のフォームからわかるように、これは単に助言でしかない。
(defconst pi 3.1415 "Pi to five places.") => pi (setq pi 3) => pi pi => 3 |
t
を返し、さもなければnil
を返す。 (ユーザーオプション向け以外の変数は、Lispプログラムの内部目的用にあり、 それらについてユーザーが知る必要はない。)
ユーザーオプション変数は、 属性variable-documentation
の最初の文字で他の変数と区別される。 その属性が存在して文字列であり、最初の文字が`*'であれば、 その変数はユーザーオプションである。
ユーザーオプション変数に属性variable-interactive
があると、 コマンドset-variable
はその属性値を使って、 変数の新しい値の読み取りを制御します。 この属性値は、interactive
の引数(see 節 20.2.1 interactive
の使い方) のように使われます。 しかしながら、この機能はdefcustom
(see 節 13. カスタマイズ定義の書き方)により ほとんど廃れています。
警告: 変数にローカル束縛があるときに スペシャルフォーム
defconst
やdefvar
を使うと、 ローカル束縛の値を変更し、グローバル束縛は変更しない。 これは望む効果ではない。 これを防ぐには、これらのスペシャルフォームはファイルのトップレベルで使う。 そうすれば、普通は有効なローカル束縛はない。 さらに、変数のローカル束縛を作るまえに、 確実にファイルをロードしておく。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
(内部に束縛を含むようなキーマップなどの)複雑な値を保持する変数を 定義し初期化するときには、つぎのように、 値の計算全体をdefvar
の内部に入れておくのが最良です。
(defvar my-mode-map (let ((map (make-sparse-keymap))) (define-key map "\C-c\C-a" 'my-command) ... map) docstring) |
この方法には、いくつかの利点があります。 まず、ファイルのロード中にユーザーが中断した場合、 変数は初期化されないか正しく初期化されるかのいずれかであり、 その中間状態ということはありません。 第2に、変数をすでに初期化したあとにファイルをロードし直しても、 変数を変更しません。 (キーをバインドし直すなどの)内容の一部を変更するために ユーザーがフックを実行した場合などには、これは重要です。 第3に、C-M-xでフォームdefvar
を評価すると、 マップを完全に初期化し直せます。
フォームdefvar
の内側に多くのコードを置くことには、欠点が1つあります。 変数の名前を指定した行から説明文字列が離れすぎてしまうことです。 つぎのようにしてこれを安全に防げます。
(defvar my-mode-map nil docstring) (if my-mode-map nil (let ((map (make-sparse-keymap))) (define-key my-mode-map "\C-c\C-a" 'my-command) ... (setq my-mode-map map))) |
これには、defvar
の内側に初期化を入れたときと同じ利点がありますが、 変数を再初期化するには、各フォームそれぞれについて C-M-xを打つ必要があります。
しかし、つぎのようなコードは書かないでください。
(defvar my-mode-map nil docstring) (if my-mode-map nil (setq my-mode-map (make-sparse-keymap)) (define-key my-mode-map "\C-c\C-a" 'my-command) ...) |
このコードでは、変数を設定してから変更しますが、 それを複数の手順で行います。 setq
の直後にユーザーが中断すると、 変数は正しく初期化されておらず、空でもnil
でもありません。 こうなったときにファイルを再ロードしても変数を初期化できません。 変数は不完全な状態のままです。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
変数を参照する普通の方法は、 変数を指名するシンボルを書くことです(see 節 8.2.2 シンボルフォーム)。 これには、プログラムを書くときに変数名を指定する必要があります。 読者は、普通このようにするでしょう。 場合によっては、実行時にどの変数を参照するか選ぶ必要があり、 そのときにはsymbol-value
を使います。
(setq abracadabra 5) => 5 (setq foo 9) => 9 ;; ここで、 |
symbolの現在の束縛が空であると、 エラーvoid-variable
を通知する。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
変数の値を変更する普通の方法は、スペシャルフォームsetq
を使うことです。 実行時に選択する変数を計算する必要があるときには、 関数set
を使います。
setq
はsymbolを評価しない。 読者が書いたシンボルに設定する。 この変数は自動的にクォートされるのである。 setq
の`q'は、『quoted(クォートする)』を表す。
フォームsetq
の値は、最後のformの値である。
(setq x (1+ 2)) => 3 x ; |
最初のformを評価して最初のsymbolに設定し、 つぎに、2番目のformを評価して2番目のsymbolに設定し、 といった具合になることに注意。
(setq x 10 ; |
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) ; |
symbol(の評価結果)が実際にはシンボルでないと、 エラーwrong-type-argument
を通知する。
(set '(x y) 'z) error--> Wrong type argument: symbolp, (x y) |
論理的には、set
はsetq
よりもさらに基本的な操作である。 どんなsetq
の使い方でも、set
で素直に書き直せる。 setq
は、set
を使ってマクロとして定義することも可能である。 しかし、set
そのものを使うことは稀であり、 初心者はset
を知る必要がほとんどない。 設定する変数を実行時に選ぶときにのみ有用である。 たとえば、コマンドset-variable
は、 ユーザーから変数名を読み取りその変数に設定するので、 set
を使う必要がある。
Common Lispに関した注意:Common Lispでは、
set
はつねにシンボルの『スペシャル』な、つまり、 動的な値を変更し、文脈上の束縛を無視する。 Emacs Lispでは、すべての変数とすべての束縛は動的であり、set
はつねに既存の最ローカルの束縛に作用する。
変数に設定する別の関数は、リストに既存でない要素を追加するように 設計されたものです。
引数symbolは暗黙にクォートされない。 add-to-list
は、set
のように普通の関数であり、 setq
とは違う。 必要ならば、読者自身でクォートする。
add-to-list
の使い方を以下に示します。
(setq foo '(a b)) => (a b) (add-to-list 'foo 'c) ;; |
(add-to-list 'var value)
に等価な式はつぎのとおりです。
(or (member value var) (setq var (cons value var))) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
あるシンボルfoo
は、さまざまなローカルな変数束縛を持つことができます。 Lispプログラムの異なる場所で確立されたものや グローバル束縛です。 もっとも最近に確立した束縛が他のものに優先します。
Emacs Lispのローカル束縛は、無限のスコープ(indefinite scope)と 動的存続期間(dynamic extent)を持ちます。 スコープ(scope)とは、ソースコードのテキスト上の どこから束縛を参照できるかを表します。 無限のスコープとは、プログラムのどこからでも変数束縛を参照できることを 意味します。 存続期間(extent)とは、プログラムの実行にしたがって、 いつ束縛が存在するかを表します。 動的存続期間とは、束縛を作成した構造が有効である限り、 束縛が存続することを意味します。
動的存続期間と無限のスコープの組み合せを 動的スコープ(dynamic scoping)と呼びます。 対照的に、ほとんどのプログラム言語は、 レキシカルスコープ(lexical scoping)を用います。 つまり、ローカル変数の参照は、 その変数を束縛する関数やブロックのテキスト上で内側にある必要があります。
Common Lispに関した注意:
Common Lispでは、『スペシャル』と宣言した変数は、 Emacs Lispのすべての変数と同様に、動的スコープである。
10.9.1 スコープ Scope means where in the program a value is visible. Comparison with other languages. 10.9.2 存続期間 Extent means how long in time a value exists. 10.9.3 動的スコープの実装 Two ways to implement dynamic scoping. 10.9.4 動的スコープの正しい使い方 How to use dynamic scoping carefully and avoid problems.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
Emacs Lispでは、 ローカル変数束縛は無限のスコープ(indefinite scope)です。 つまり、プログラムテキスト上のどの関数からでも、 ある変数束縛を参照できるのです。 つぎの関数定義を考えてみましょう。
(defun binder (x) ; |
テキスト上のスコープを用いる言語では、 binder
内のx
の束縛を、user
で参照することはできません。 なぜなら、user
は、テキスト上で関数binder
の内側にはないからです。 しかしながら、動的スコープのEmacs Lispでは、 状況に応じて、binder
内で確立したx
の束縛を user
から参照してもしなくてもよいのです。
binder
をまったく呼び出さずに、直接user
を呼び出したときには、 とにかくみつかったx
の束縛を使うが、 それはbinder
のものではありえない。
foo
をつぎのように定義してbinder
を呼び出したときには、 binder
が作った束縛をuser
で見える。
(defun foo (lose) (user)) |
foo
をつぎのように定義してbinder
を呼び出したときには、 binder
が作った束縛はuser
では見えない。
(defun foo (x) (user)) |
ここで、binder
がfoo
を呼び出すと、 foo
はx
を束縛する。 (foo
の束縛はbinder
の束縛を隠す(shadow)という。) したがって、user
は、binder
の束縛ではなく、 foo
の束縛を参照することになる。
Emacs Lispで動的スコープを使うのは、 テキスト上のスコープの単純な実装は遅いからです。 さらに、すべてのLispシステムは、少なくともオプションとして、 動的スコープを使えるようにする必要があります。 テキスト上のスコープが標準であると、 特定の変数に対して動的スコープを指定する方法が必要になります。 Emacsで両方のスコープを使えるようにしてもよいのですが、 動的スコープだけだと実装がより簡単になります。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
存続期間(Extent)とは、プログラムの実行中において、 変数名が有効である期間を指します。 Emacs Lispでは、束縛を作ったフォームを実行している期間中だけ、 変数は有効です。 これを動的存続期間(dynamic extent)と呼びます。 CやPascalなどのほとんどの言語の『ローカル』変数や『自動』変数も 動的存続期間です。
動的存続期間とは別のものに無限の存続期間(indefinite extent)があります。 つまり、変数束縛は、その束縛を作ったフォームから抜けても存続するのです。 たとえば、Common LispやSchemeにはこれがありますが、Emacs Lispにはありません。
これを説明するために、つぎの関数make-add
を考えます。 この関数は、nに自身の引数mを加算する関数を返します。 この関数はCommon Lispでは動作しますが、Emacs Lispではだめです。 というのは、make-add
の呼び出しを抜けると、 変数nは実引数2に束縛されなくなるからです。
(defun make-add (n) (function (lambda (m) (+ n m)))) ; 関数を返す => make-add (fset 'add2 (make-add 2)) ; 関数 |
Lispの方言のいくつかには『クロージャ』(closure)があります。 それは関数のようなオブジェクトですが、追加の変数束縛を記録します。 Emacs Lispにはクロージャはありません。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
(Emacs Lispの実際の動作とは異なるが)単純な実装例が、 動的束縛を理解する助けになるでしょう。 この技法を深い束縛(ディープバインディング、deep binding)と呼び、 初期のLispシステムで使われていました。
変数・値の対である束縛のスタックがあるとしましょう。 関数やフォームlet
に入ると、 引数やローカル変数の束縛をスタックに積みます。 束縛を作った構造から抜けるとそれらの束縛を取りさります。
変数の値は、スタックの先頭から底へ向けてその変数の束縛を探索します。 その束縛から得る値が変数の値になります。 変数に設定するには、現在の束縛を探して、その束縛に新たな値を格納します。
これからわかるように、関数の束縛は、その関数の実行中には、 たとえ別の関数を呼び出していても、存続しています。 これが束縛の存続が動的であるという理由です。 また、その束縛が有効である期間中ならば、同じ変数を使えば他の関数からも 束縛を参照できるのです。 これがスコープが無限であるという理由です。
GNU Emacs Lispにおいて、変数のスコープの実際の実装には、 浅い束縛(シャローバインディング、shallow binding)と呼ばれる 技法を用いています。 各変数には現在値を保存しておく標準の場所、シンボルの値セルがあります。
浅い束縛では、変数の設定は値セルに値を格納することで動作します。 新たな束縛を作成すると(以前の束縛に属する)古い値をスタックに積み、 新たなローカル値を値セルに格納します。 束縛を解くときには、古い値をスタックから取り出して値セルに格納します。
浅い束縛を用いる理由は、束縛を探索する必要がないため、 深い束縛と同じ結果を持ちながら高速に動作するからです。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
ある関数で変数を束縛し別の関数でそれを使うことは、強力な技法ですが、 なんの制限もせずに使うとプログラムを理解し難いものにしてしまいます。 この技法を見通しよく使うための2つの方法があります。
他のプログラマに対して、彼らがそのような変数を目にするまえに、 そのような変数の使い方がわかるようなコメントを書き、 他の場所では使わないように助言しておく。
case-fold-search
は、 『nil
以外であれば探索時に大文字小文字を区別しない』と定義されている。 さまざまな探索関数や置換関数が、この変数を直接に、あるいは、 サブルーティンを介して参照するが、 この変数を束縛したり設定したりしない。
こうしておいて別のプログラムで変数を束縛するが、 それにどのような効果があるか確実に知ってから行える。
いずれの場合でも、変数はdefvar
で定義するべきです。 これは、関数間での変数の使い方を見るように伝えることで、 他人が読者のプログラムを理解するのを助けます。 また、バイトコンパイラからの警告も防ぎます。 変数名が衝突しないようにも注意しましょう。 x
のような短い名前を使わないでください。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
グローバルとローカルの変数束縛は、 ほとんどのプログラム言語にいろいろな形であります。 Emacsには、あまり普通でない追加の種類の変数束縛があります。 1つのバッファだけに適用されるバッファローカルな束縛、 1つのフレームだけに適用されるフレームローカルな束縛です。 異なるバッファやフレームごとに変数に異なる値があるということは、 重要なカスタマイズ技法です。
本節では、バッファローカルな束縛を説明します。 フレームローカルな束縛については、つぎの節と See 節 10.11 フレームローカルな変数。 (各端末にローカルな束縛を持つ変数も少数ある。 see 節 28.2 複数ディスプレイ。)
10.10.1 バッファローカルな変数の紹介 Introduction and concepts. 10.10.2 バッファローカルな束縛の作成と削除 Creating and destroying buffer-local bindings. 10.10.3 バッファローカル変数のデフォルト値 The default value is seen in buffers that don't have their own buffer-local values.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
バッファローカルな変数には、特定のバッファに関連したバッファローカルな 束縛があります。 この束縛は、そのバッファがカレントバッファであるときに有効になります。 さもなければなんの効果もありません。 バッファローカルな束縛が有効なときに変数に設定すると、 新しい値はその束縛に入り、他の束縛は変更されません。 つまり、その変更は、変更を行ったバッファだけで見ることができるのです。
変数の通常の束縛、つまり、特定のバッファに関連していない束縛を デフォルトの束縛(default binding)と呼びます。 多くの場合、これはグローバル束縛です。
変数は、あるバッファ群ではバッファローカルな束縛を持ち、 他のバッファではそのような束縛を持たないようにできます。 変数に対する独自の束縛を持たないバッファすべてでは、 デフォルトの束縛を共有します。 (これには、新たに作成されるバッファも含む。) バッファローカルな束縛を持たないバッファで変数に設定すると、 (状況を複雑にするフレームローカルな束縛はないと仮定して) デフォルトの束縛を使います。 したがって、新たな値はデフォルトの束縛を見るバッファすべてで見えます。
バッファローカルな束縛のもっとも一般的な使い方は、 メジャーモードでコマンドのふるまいを制御する変数に変更することです。 たとえば、CモードやLispモードでは、変数paragraph-start
を設定して、 空行だけが段落を区切るように指定します。 これには、CモードやLispモードになったバッファでは、 変数をバッファローカルにしてから、 そのモード用の新たな値を変数に設定するのです。 See 節 22.1 メジャーモード。
バッファローカルな束縛を作る普通の方法は、 make-local-variable
です。 メジャーモードのコマンドは典型的にこれを使います。 これはカレントバッファだけに影響します。 (これから作成するものも含めて)他のすべてのバッファは、 それ専用のバッファローカルな束縛を明示的に与えない限り、 デフォルト値を共有し続けます。
より強力な操作は、make-variable-buffer-local
を呼び出して 変数を自動的にバッファローカルにするように印を付けることです。 これは、これから作成するものも含めたバッファすべてで、 変数をバッファローカルにすると考えることができます。 より正確には、変数がカレントバッファにローカルでなければ、 自動的に変数をカレントバッファにローカルにするように設定する効果があります。 すべてのバッファは通常どおり変数のデフォルト値を共有して始まりますが、 変数に設定するとカレントバッファにバッファローカルな束縛を作ります。 新たな値はバッファローカルな束縛に格納され、デフォルトの束縛は変更しません。 つまり、どのバッファでもデフォルト値を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 ; 消えているべきローカルな
|
しかし、つぎに示すようにsave-excursion
を使えば、この問題を回避できます。
(let ((foo 'temp)) (save-excursion (set-buffer "b") body...)) |
body内でのfoo
への参照は、 バッファ`b'のバッファローカルな束縛を使います。
ファイルでローカル変数の値を指定していると、 そのファイルを訪問したときに、それらはバッファローカルな値になります。 See GNU Emacs マニュアル。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
variableのバッファローカルな値は、 variableの以前と同じ値で始まる。 variableが空であれば、空のままである。
;; バッファ`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
に入るときや抜けるときに カレントバッファになっていないと、正しく動作しない。 これは、let
が、異なる種類の束縛を区別しないからであり、 どの変数の束縛を作るかだけを知っているからである。
変数が端末にローカルなときには、この関数はエラーを通知する。 そのような変数は、同時にバッファローカルな束縛を持てない。 See 節 28.2 複数ディスプレイ。
注意: フック変数に対して
make-local-variable
を使わないこと。 そのかわりにmake-local-hook
を使う。 see 節 22.6 フック。
この機能の重要な点は、 (let
や他の束縛を作る構文で)変数を束縛しても、 その変数のバッファローカルな束縛を作らないことである。 (set
やsetq
で)変数を設定して初めてそのようにする。
返す値はvariableである。
警告: ユーザーが異なるバッファでは異なったカスタマイズをするかもしれないと いうだけで、ユーザーオプション変数に
make-variable-buffer-local
を 使うべきだと仮定しないこと。 ユーザーは、必要ならば、どんな変数でもローカルにできる。 選択はユーザーに任せるのがよい。
2つのバッファが同じ束縛を共有しないことが重要な場面では、 make-variable-buffer-local
を使う。 たとえば、異なるバッファでは異なる値を持つことに依存するような Lispプログラムで内部目的に変数を使うときには、 make-variable-buffer-local
を使うのが最良である。
t
を返し、 さもなければnil
を返す。
(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") ... ;; 続いて、組み込みでないバッファローカルな変数 ;; これはバッファローカルで、かつ、空 foobar ;; これはバッファローカルで、かつ、空ではない (bind-me . 69)) |
このリストのコンスセルのCDRに新たな値を格納しても、 変数のバッファローカルな値を変更しないことに注意してほしい。
自動的にバッファローカルにする印が付いた変数のバッファローカルな束縛を 削除すると、カレントバッファではデフォルト値が見えるようになる。 しかし、変数に再度設定すると、それに対するバッファローカルな束縛が 再度作成される。
kill-local-variable
はvariableを返す。
この関数がコマンドであるのは、 対話的にバッファローカルな変数を作るのが有用なように、 対話的にバッファローカルな変数を削除するのが有用な場合があるからである。
この関数は、バッファに属する他のある種の情報もリセットする。 つまり、ローカルキーマップにnil
、 構文テーブルに(standard-syntax-table)
の値、 大文字小文字テーブルに(standard-case-table)
、 略語テーブルにfundamental-mode-abbrev-table
の値を設定する。
この関数が最初に行うことは、 ノーマルフックchange-major-mode-hook
(下記参照)を 実行することである。
各メジャーモードコマンドはこの関数を呼び出すことから始める。 つまり、基本(fundamental)モードに切り替え、 それ以前のメジャーモードのほとんどの効果を消しさる。 この処理を保証するために、メジャーモードで設定する変数には、 恒久的の印を付けないこと。
kill-all-local-variables
はnil
を返す。
kill-all-local-variables
は、最初にこのノーマルフックを実行する。 このフックはメジャーモードに対して、 ユーザーが別のメジャーモードに切り替えていた場合には、 なにか特別なことを行う情報を提供する。 最良の結果を得るためには、この変数をバッファローカルにしておくと その役目を終えると変数は消えてしまい、それ以降のメジャーモードに干渉しない。 see 節 22.6 フック。 バッファローカル変数は、 変数名(シンボル)の属性permanent-local
がnil
以外であると、 恒久的(permanent)です。 恒久的なローカル変数は、編集作業の文脈ではなく、 どのファイルを訪問中であるとかどのように保存するとかに関連する情報に 適しています。
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
バッファローカルな束縛がある変数のグローバル値を、 デフォルト値とも呼びます。 カレントバッファや選択したフレームに変数の独自の束縛がない場合に、 グローバル値を使うからです。
関数default-value
と関数setq-default
は、 カレントバッファにバッファローカルな束縛があるかどうかに関わらず、 変数のデフォルト値を参照したり変更したりします。 たとえば、setq-default
を使って、 ほとんどのバッファのparagraph-start
のデフォルト値を変更できます。 この変数のバッファローカルな値があるCモードやLispモードのバッファで 行ってもこれは動作します。
スペシャルフォームdefvar
やdefconst
も、 バッファローカルやフレームローカルな値ではなく、 (変数に設定する場合には)デフォルト値を設定します。
symbol-value
(see 節 10.7 変数値の参照)と等価。default-boundp
は、 symbolのデフォルト値が空でないことを調べる。 (default-boundp 'foo)
がnil
を返せば、 (default-value 'foo)
はエラーになる。
default-boundp
は、boundp
がsymbol-value
に対応するように、 default-value
に対応する。
setq-default
の値は、最後のformの値である。
symbolがカレントバッファでバッファローカルではなく、かつ、 自動的にバッファローカルにする印が付いていなければ、 setq-default
はsetq
と同じ効果がある。 symbolがカレントバッファでバッファローカルならば、 (バッファローカルな値を持たない)別のバッファが見る値を変更し、 カレントバッファが見る値は変更しない。
;; バッファ`foo'において、 (make-local-variable 'buffer-local) => buffer-local (setq buffer-local 'value-in-foo) => value-in-foo (setq-default buffer-local 'new-default) => new-default buffer-local => value-in-foo (default-value 'buffer-local) => new-default ;; (新たな)バッファ`bar'では、 buffer-local => new-default (default-value 'buffer-local) => new-default (setq buffer-local 'another-default) => another-default (default-value 'buffer-local) => another-default ;; バッファ`foo'に戻ってみると buffer-local => value-in-foo (default-value 'buffer-local) => another-default |
setq-default
に似ているが、 symbolは普通どおりに評価される引数である。
(set-default (car '(a b c)) 23) => 23 (default-value 'a) => 23 |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
変数にバッファローカルな束縛があるように、 変数にはフレームローカルな束縛もあります。 これらの束縛は1つのフレームに属し、 そのフレームを選択しているときに有効になります。 フレームローカルな束縛は、実際にはフレームパラメータです。 特定のフレームでフレームローカルな束縛を作るには modify-frame-parameters
を呼び出し、 パラメータ名として変数名を指定します。
特定の変数に対するフレームローカルな束縛を有効にするには、 関数make-variable-frame-local
を呼び出します。
変数が端末にローカルであると、この関数はエラーを通知する。 そのような変数はフレームローカルな束縛を同時には持てないからである。 see 節 28.2 複数ディスプレイ。 Emacsで特別に実装されている少数の変数は(普通) バッファローカルになることができるが、フレームローカルにはならない。
バッファローカルな束縛はフレームローカルな束縛に優先します。 変数foo
を考えてみましょう。 カレントバッファにfoo
のバッファローカルな束縛があると、 その束縛が有効になります。 選択したフレームにfoo
のフレームローカルな束縛があると、 その束縛が有効になります。 さもなければ、foo
のデフォルトの束縛が有効になります。
つぎに例を示します。 まず、foo
の束縛を準備しておきます。
(setq f1 (selected-frame)) (make-variable-frame-local 'foo) ;; `b1'において、 |
では、さまざまな文脈でfoo
を調べてみましょう。 バッファ`b1'がカレントバッファであれば、 選択したフレームに関係なく、 `b1'のバッファローカルな束縛が有効になっています。
(select-frame f1) (set-buffer (get-buffer-create "b1")) foo => (b 1) (select-frame f2) (set-buffer (get-buffer-create "b1")) foo => (b 1) |
さもなければ、フレームの束縛を使う可能性があります。 フレームf2
を選択していると、 そのフレームローカルな束縛が有効になります。
(select-frame f2) (set-buffer (get-buffer "*scratch*")) foo => (f 2) |
カレントバッファにもフレームにも束縛がなければ、 デフォルトの束縛を使います。
(select-frame f1) (set-buffer (get-buffer "*scratch*")) foo => nil |
変数の有効な束縛がフレームローカルな束縛であるとき、 変数に設定するとその束縛を変更します。 frame-parameters
でその結果を見ることができます。
(select-frame f2) (set-buffer (get-buffer "*scratch*")) (setq foo 'nobody) (assq 'foo (frame-parameters f2)) => (foo . nobody) |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |
フレームに分類されるものでローカルな束縛というアイデアを考察しています。 たとえば、すべてのカラーフレーム、暗い背景色のすべてのフレームなどです。 この機能が本当に有用なのか明らかでないので、それらをまだ実装してはいません。 after-make-frame-hook
に関数を追加して、 各フレームの適切な状態に応じたフレームパラメータを設定すれば、 同じような結果を得られます。
ウィンドウローカルな束縛を実装することも可能です。 これが有用である多くの状況を知りませんが、 バッファローカルな束縛を持つ間接バッファ(see 節 26.11 間接バッファ (2003/10/30))で、 そのような状況をより堅牢に扱えると思います。
これら2種類のローカル束縛のいずれかを必要とする十分な数のアプリケーションが みつかれば、Emacsの将来の版でそのような束縛を提供するでしょう。
[ << ] | [ >> ] | [表紙] | [目次] | [索引] | [検索] [上端 / 下端] |