[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10. 変数

変数(variable)は, プログラムにおいて値を表すために使う名前です. ほとんどすべてのプログラム言語には, ある種の変数があります. Lispプログラムのテキストでは, シンボルの構文を使って変数を書きます.

ほとんどのプログラム言語と違って, Lispでは, プログラムはLispオブジェクトで表現し, テキスト表現は副次的なものです. 変数として使うLispオブジェクトはシンボルです. シンボル名が変数名であり, 変数の値はシンボルの値セルに格納されています. 変数としてのシンボルの使い方は, 関数名としての使い方とは独立しています. See section シンボルの構成要素.

Lispプログラムを構成するLispオブジェクト群は, プログラムのテキスト表現を決定します. つまり, Lispオブジェクト群に対する単なる入力構文です. これは, たとえば, Lispプログラムのテキスト表現では, 変数を表現するシンボルの入力構文で変数を書く理由です.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.1 グローバル変数

変数を使うもっとも簡単な方法は, グローバルに(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

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.2 変更不可能な変数

Emacs Lispには, 通常それ自身に評価されるある種のシンボルがあります. ‘:’で始まる名前の任意の変数, および, niltです. これらのシンボルを再束縛することはできず, それらの値を変更することもできません. niltを設定しようとしたり束縛しようとすると, エラーsetting-constantを通知します. ‘:’で始まる名前のシンボルに関してもそうですが, そのようなシンボルにそれ自身を設定することはできます.

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

この変数がnilであると, ‘:’で始まる名前の変数を望みの値に設定したり束縛したりできる. これは, そのようなことを行う古いLispプログラムの実行を可能にするためである.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.3 ローカル変数

グローバル変数は, 明示的に新しい値で置き換えない限り存続する値を持ちます. 一時的にしか存在しない変数値, つまり, プログラムのある部分を完了するまでのみ存在する変数値を 作れると便利なことがあります. このような値をローカル(local, 局所的)と呼び, そのように使われる変数をローカル変数(local variables)と呼びます.

たとえば, 関数を呼び出したとき, その引数変数は, 関数を抜けるまで存続する新たなローカルな値を受け取ります. スペシャルフォームletは, 指定した変数の新たなローカル値を 明示的に確立します. これらはフォームletを抜けるまで存続します.

ローカル値を確立すると, 変数の以前の値(あるいは値がないこと)を保存します. ローカル値の存続期間が終了すると, 以前の値を復元します. この期間は, 以前の値を隠して(shadowed)いて 以前の値は見えません. グローバル値でもローカル値でも隠せます(see section スコープ).

変数がローカルなときに(setqなどで)その変数を設定すると, ローカル値を置き換えます. 隠されているグローバル値や以前のローカル値を変更しません. このふるまいをモデル化するために, 変数のローカル値に加えて変数のローカル束縛(local binding)を 考えます.

ローカル束縛とは, ローカル値を保持する概念的な場所です. 関数やletなどのスペシャルフォームに入るたびに ローカル束縛を作成します. 関数やフォームletから抜けるとローカル束縛を削除します. ローカル束縛が存続する限り, 変数の値はそこに保持されています. ローカル束縛が存在するときにsetqsetを使うと, ローカル束縛の中に別の値を格納します. 新たな束縛を作るのではありません.

グローバル値を保持する概念的な場所を グローバル束縛(global binding)ともいいます.

変数には一度に複数のローカル束縛がありえます (たとえば, 同じ変数を束縛する入れ子になったフォームletがあるとき). そのような場合, 既存のもっとも最近に作成されたローカル束縛が, 変数の現在の束縛(current binding)です. (この規則を動的スコープ(dynamic scoping)と呼びます. see section 変数束縛のスコープルール) ローカル束縛がまったくなければ, 変数のグローバル束縛が現在の束縛です. 現在の束縛のことを強調して既存の最ローカル束縛と呼ぶこともあります. シンボルの通常の評価では, その現在の束縛の値を返します.

スペシャルフォームletlet*は, ローカル束縛を作るためにあります.

Special Form: let (bindings…) forms…

このスペシャルフォームは, bindingsに従って変数を束縛し, formsのすべてをテキスト上の順に評価する. letフォームは, formsの最後のフォームの値を返す.

bindingsのおのおのは, (i)シンボルであるか, (ii)フォーム(symbol value-form)のリストである. 前者は, シンボルにnilを束縛する. 後者は, symbolvalue-formの評価結果を束縛する. value-formを省略するとnilを使う.

bindingsvalue-form群すべてを現れる順に評価してから, シンボルにそれらの値を束縛する. 例をつぎに示す. Zは, Yの古い値2に束縛され, Yの新しい値1ではない.

 
(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 フレームローカルな変数)を持つことができます. 少数の変数は, 端末にローカルな束縛(see section 複数ディスプレイ) を持つこともできます. この種の束縛は普通のローカル束縛と同じように働きますが, これらはEmacsの『どの部分』にいるかに依存したローカル化であり, 時間的なローカル化ではありません.

Variable: max-specpdl-size

この変数は, ("Variable binding depth exceeds max-specpdl-size"を 伴った)エラーを通知するまでに許される, ローカル変数束縛と unwind-protectによる後始末(see section 非ローカル脱出)の 全体の個数の制限を定義する.

この制限, および, これを超えたときのエラーは, 不正に定義された関数によってLispが無限に再帰することを防止する 1つの方法である.

デフォルト値は600である. Lispデバッガに入ったとき, 制限に近い場合にはデバッガ自身が実行できることを保証するために値を増やす.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.4 変数が『空』であるとき

シンボルにグローバル変数としての値を一度も与えていないとき, そのシンボルのグローバル値は(void)であるといいます. いいかえれば, シンボルの値セルにはどんなLispオブジェクトも入っていません. シンボルを評価しようとすると, 値ではなくエラーvoid-variableを得ます.

nilという値は空とは異なることに注意してください. シンボルnilはLispオブジェクトであり, 他のオブジェクトと同様に 変数の値になりえます. それはなのです. 空な変数はいかなる値も持たないのです.

変数に値を与えたあとでは, makunboundを使って 再度その変数を空にできます.

Function: makunbound symbol

この関数は, symbolの現在の変数束縛を空にする. これ以降に変数としてこのシンボルの値を使おうとすると, 再度設定していない限り, エラーvoid-variableを通知する.

makunboundsymbolを返す.

 
(makunbound 'x)      ; 変数xのグローバル値を空にする
     ⇒ x
x
error--> Symbol's value as variable is void: 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)                     ; 外側のletの束縛が見える
     ⇒ 2

makunboundで空にした変数は, 一度も値を受け取ったことがなく, そのために空である変数と区別できません.

変数が現在, 空であるかどうかは関数boundpを使って調べられます.

Function: boundp variable

boundpは, (シンボル)variableが空でなければ, より正確にいえば, 現在の束縛が空でなければtを返す. さもなければnilを返す.

 
(boundp 'abracadabra)          ; 空で始める
     ⇒ nil
(let ((abracadabra 5))         ; ローカルに束縛する
  (boundp 'abracadabra))
     ⇒ t
(boundp 'abracadabra)          ; グローバルにはまだ空である
     ⇒ nil
(setq abracadabra 5)           ; グローバルに空でなくする
     ⇒ 5
(boundp 'abracadabra)
     ⇒ t

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.5 グローバル変数を定義する

スペシャルフォームdefconstdefvar変数定義を使って, シンボルをグローバル変数として使う意図を表明できます.

Emacs Lispでは, 定義には3つの目的があります. まず, コードを読む人向けに, 特定のシンボルを(変数として)特定目的に 使う意図があることを知らせます. 第2に, Lispシステムに対しては, 値と説明文字列を提供して これらのことを伝えます. 第3に, プログラム内の関数や変数のデータベースを作成する etagsmake-docfileなどのユーティリティに情報を提供します.

defconstdefvarの違いは, 主に好みの問題であり, 値が変更されるかどうかを人に伝えます. Emacs Lispは, defconstdefvarの宣言に基づいて 変数の使い方を制限することはしません. しかしながら, 初期化に関しては違いがあります. defconstは無条件に変数を初期化しますが, defvarは変数が空である場合にのみ初期化します.

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

このスペシャルフォームは, symbolを変数として定義し, 初期値や説明文字列を設定する. この定義は, コードを読む人向けに, 値を設定したり変更する変数としてsymbolを使うことを伝える. symbolは評価されないことに注意. 定義するシンボルは, defvarに明示的に現れる必要がある.

symbolの値が空でありvalueを指定してあると, defvarvalueを評価し, その結果をsymbolに設定する. しかし, symbolにすでに値があれば(つまり, 空でなければ), valueをまったく評価せず, symbolの値も変更しない. valueを省略した場合, symbolの値をいっさい変更しない.

symbolにカレントバッファでバッファローカルな束縛がある場合には, defvarはデフォルト値に作用する. それは, バッファには独立であり, 現在の(バッファローカルな)束縛ではない. defvarは, デフォルト値が空の場合にデフォルト値を設定する. see section バッファローカルな変数.

emacs-lispモードにおいてC-M-xeval-defun)でトップレベルの フォームdefvarを評価すると, eval-defunの特別な機能により, 変数の値が空かどうかを調べずに無条件に変数に設定する.

doc-stringがあれば, それは変数の説明文を指定する. (説明文を指定できるのは, 変数定義の主な利点の1つである. ) 説明文はシンボルの属性variable-documentationに格納する. Emacsのヘルプ関数(see section 説明文)は, この属性を調べる.

doc-stringの最初の文字が‘*’であると, この変数をユーザーオプションと考えることを意味する. これにより, ユーザーはコマンドset-variableedit-optionsを 使って簡単に変数を設定できる. しかしながら, ユーザーオプションの変数には, defvarではなくdefcustomを使ったほうがよく, そうすればカスタマイズ情報を指定できる. see section カスタマイズ定義の書き方.

いくつか例をあげる. つぎのフォームは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)

フォームdefvarsymbolを返すが, 通常このフォームはファイルのトップレベルで使われ, そこでは値は関係ない.

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

このスペシャルフォームは, symbolを変数として定義し初期化する. この定義は, コードを読む人向けに, symbolはこれ以降標準のグローバル値を持ち, ユーザーや他のプログラムが変更すべきでないことを伝える. 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
Function: user-variable-p variable

この関数は, variableがユーザーオプション, つまり, カスタマイズのためにユーザーが設定することを意図した変数であると, tを返し, さもなければnilを返す. (ユーザーオプション向け以外の変数は, Lispプログラムの内部目的用にあり, それらについてユーザーが知る必要はない. )

ユーザーオプション変数は, 属性variable-documentationの最初の文字で他の変数と区別される. その属性が存在して文字列であり, 最初の文字が‘*’であれば, その変数はユーザーオプションである.

ユーザーオプション変数に属性variable-interactiveがあると, コマンドset-variableはその属性値を使って, 変数の新しい値の読み取りを制御します. この属性値は, interactiveの引数(see section interactiveの使い方) のように使われます. しかしながら, この機能はdefcustom(see section カスタマイズ定義の書き方)により ほとんど廃れています.

警告: 変数にローカル束縛があるときに スペシャルフォームdefconstdefvarを使うと, ローカル束縛の値を変更し, グローバル束縛は変更しない. これは望む効果ではない. これを防ぐには, これらのスペシャルフォームはファイルのトップレベルで使う. そうすれば, 普通は有効なローカル束縛はない. さらに, 変数のローカル束縛を作るまえに, 確実にファイルをロードしておく.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.6 変数を堅牢に定義するためのヒント

(内部に束縛を含むようなキーマップなどの)複雑な値を保持する変数を 定義し初期化するときには, つぎのように, 値の計算全体を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でもありません. こうなったときにファイルを再ロードしても変数を初期化できません. 変数は不完全な状態のままです.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.7 変数値の参照

変数を参照する普通の方法は, 変数を指名するシンボルを書くことです(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

symbolの現在の束縛が空であると, エラーvoid-variableを通知する.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.8 変数値の変更

変数の値を変更する普通の方法は, スペシャルフォームsetqを使うことです. 実行時に選択する変数を計算する必要があるときには, 関数setを使います.

Special Form: setq [symbol form]…

このスペシャルフォームは, 変数の値を変更するもっとも一般的な方法である. 各symbolに, 対応するformの評価結果である新たな値を与える. シンボルの既存の際, ローカルの束縛を変更する.

setqsymbolを評価しない. 読者が書いたシンボルに設定する. この変数は自動的にクォートされるのである. setqの‘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に設定し, つぎに, 2番目のformを評価して2番目のsymbolに設定し, といった具合になることに注意.

 
(setq x 10          ; xは, yの値を計算するまえに
      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で素直に書き直せる. setqは, setを使ってマクロとして定義することも可能である. しかし, setそのものを使うことは稀であり, 初心者はsetを知る必要がほとんどない. 設定する変数を実行時に選ぶときにのみ有用である. たとえば, コマンドset-variableは, ユーザーから変数名を読み取りその変数に設定するので, setを使う必要がある.

Common Lispに関した注意: Common Lispでは, setはつねにシンボルの『スペシャル』な, つまり, 動的な値を変更し, 文脈上の束縛を無視する. Emacs Lispでは, すべての変数とすべての束縛は動的であり, setはつねに既存の最ローカルの束縛に作用する.

変数に設定する別の関数は, リストに既存でない要素を追加するように 設計されたものです.

Function: add-to-list symbol element

この関数は, elementが変数symbolの値のリストのメンバでなければ, elementと変数symbolの値をコンスした値を 変数symbolに設定する. リストを変更してもしなくても結果のリストを返す. 呼び出すまえに, symbolの値はリストであるほうがよい.

引数symbolは暗黙にクォートされない. add-to-listは, setのように普通の関数であり, 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)))

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.9 変数束縛のスコープルール

あるシンボルfooは, さまざまなローカルな変数束縛を持つことができます. Lispプログラムの異なる場所で確立されたものや グローバル束縛です. もっとも最近に確立した束縛が他のものに優先します.

Emacs Lispのローカル束縛は, 無限のスコープ(indefinite scope)と 動的存続期間(dynamic extent)を持ちます. スコープ(scope)とは, ソースコードのテキスト上の どこから束縛を参照できるかを表します. 無限のスコープとは, プログラムのどこからでも変数束縛を参照できることを 意味します. 存続期間(extent)とは, プログラムの実行にしたがって, いつ束縛が存在するかを表します. 動的存続期間とは, 束縛を作成した構造が有効である限り, 束縛が存続することを意味します.

動的存続期間と無限のスコープの組み合せを 動的スコープ(dynamic scoping)と呼びます. 対照的に, ほとんどのプログラム言語は, レキシカルスコープ(lexical scoping)を用います. つまり, ローカル変数の参照は, その変数を束縛する関数やブロックのテキスト上で内側にある必要があります.

Common Lispに関した注意: Common Lispでは, 『スペシャル』と宣言した変数は, Emacs Lispのすべての変数と同様に, 動的スコープである.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.9.1 スコープ

Emacs Lispでは, ローカル変数束縛は無限のスコープ(indefinite scope)です. つまり, プログラムテキスト上のどの関数からでも, ある変数束縛を参照できるのです. つぎの関数定義を考えてみましょう.

 
(defun binder (x)   ; xは, binderで束縛
   (foo 5))         ; fooは別の関数
(defun user ()      ; xは, userにおいて『自由』
  (list x))

テキスト上のスコープを用いる言語では, binder内のxの束縛を, userで参照することはできません. なぜなら, userは, テキスト上で関数binderの内側にはないからです. しかしながら, 動的スコープのEmacs Lispでは, 状況に応じて, binder内で確立したxの束縛を userから参照してもしなくてもよいのです.

Emacs Lispで動的スコープを使うのは, テキスト上のスコープの単純な実装は遅いからです. さらに, すべてのLispシステムは, 少なくともオプションとして, 動的スコープを使えるようにする必要があります. テキスト上のスコープが標準であると, 特定の変数に対して動的スコープを指定する方法が必要になります. Emacsで両方のスコープを使えるようにしてもよいのですが, 動的スコープだけだと実装がより簡単になります.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.9.2 存続期間

存続期間(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))  ; 関数add2
                           ;   (make-add 2)を使って定義する
     ⇒ (lambda (m) (+ n m))
(add2 4)                   ; 4に2を加算してみる
error--> Symbol's value as variable is void: n

Lispの方言のいくつかには『クロージャ』(closure)があります. それは関数のようなオブジェクトですが, 追加の変数束縛を記録します. Emacs Lispにはクロージャはありません.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.9.3 動的スコープの実装

(Emacs Lispの実際の動作とは異なるが)単純な実装例が, 動的束縛を理解する助けになるでしょう. この技法を深い束縛(ディープバインディング, deep binding)と呼び, 初期のLispシステムで使われていました.

変数・値の対である束縛のスタックがあるとしましょう. 関数やフォームletに入ると, 引数やローカル変数の束縛をスタックに積みます. 束縛を作った構造から抜けるとそれらの束縛を取りさります.

変数の値は, スタックの先頭から底へ向けてその変数の束縛を探索します. その束縛から得る値が変数の値になります. 変数に設定するには, 現在の束縛を探して, その束縛に新たな値を格納します.

これからわかるように, 関数の束縛は, その関数の実行中には, たとえ別の関数を呼び出していても, 存続しています. これが束縛の存続が動的であるという理由です. また, その束縛が有効である期間中ならば, 同じ変数を使えば他の関数からも 束縛を参照できるのです. これがスコープが無限であるという理由です.

GNU Emacs Lispにおいて, 変数のスコープの実際の実装には, 浅い束縛(シャローバインディング, shallow binding)と呼ばれる 技法を用いています. 各変数には現在値を保存しておく標準の場所, シンボルの値セルがあります.

浅い束縛では, 変数の設定は値セルに値を格納することで動作します. 新たな束縛を作成すると(以前の束縛に属する)古い値をスタックに積み, 新たなローカル値を値セルに格納します. 束縛を解くときには, 古い値をスタックから取り出して値セルに格納します.

浅い束縛を用いる理由は, 束縛を探索する必要がないため, 深い束縛と同じ結果を持ちながら高速に動作するからです.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.9.4 動的スコープの正しい使い方

ある関数で変数を束縛し別の関数でそれを使うことは, 強力な技法ですが, なんの制限もせずに使うとプログラムを理解し難いものにしてしまいます. この技法を見通しよく使うための2つの方法があります.

いずれの場合でも, 変数はdefvarで定義するべきです. これは, 関数間での変数の使い方を見るように伝えることで, 他人が読者のプログラムを理解するのを助けます. また, バイトコンパイラからの警告も防ぎます. 変数名が衝突しないようにも注意しましょう. xのような短い名前を使わないでください.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

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

グローバルとローカルの変数束縛は, ほとんどのプログラム言語にいろいろな形であります. Emacsには, あまり普通でない追加の種類の変数束縛があります. 1つのバッファだけに適用されるバッファローカルな束縛, 1つのフレームだけに適用されるフレームローカルな束縛です. 異なるバッファやフレームごとに変数に異なる値があるということは, 重要なカスタマイズ技法です.

本節では, バッファローカルな束縛を説明します. フレームローカルな束縛については, つぎの節と See section フレームローカルな変数. (各端末にローカルな束縛を持つ変数も少数ある. see section 複数ディスプレイ. )


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.10.1 バッファローカルな変数の紹介

バッファローカルな変数には, 特定のバッファに関連したバッファローカルな 束縛があります. この束縛は, そのバッファがカレントバッファであるときに有効になります. さもなければなんの効果もありません. バッファローカルな束縛が有効なときに変数に設定すると, 新しい値はその束縛に入り, 他の束縛は変更されません. つまり, その変更は, 変更を行ったバッファだけで見ることができるのです.

変数の通常の束縛, つまり, 特定のバッファに関連していない束縛を デフォルトの束縛(default binding)と呼びます. 多くの場合, これはグローバル束縛です.

変数は, あるバッファ群ではバッファローカルな束縛を持ち, 他のバッファではそのような束縛を持たないようにできます. 変数に対する独自の束縛を持たないバッファすべてでは, デフォルトの束縛を共有します. (これには, 新たに作成されるバッファも含む. ) バッファローカルな束縛を持たないバッファで変数に設定すると, (状況を複雑にするフレームローカルな束縛はないと仮定して) デフォルトの束縛を使います. したがって, 新たな値はデフォルトの束縛を見るバッファすべてで見えます.

バッファローカルな束縛のもっとも一般的な使い方は, メジャーモードでコマンドのふるまいを制御する変数に変更することです. たとえば, CモードやLispモードでは, 変数paragraph-startを設定して, 空行だけが段落を区切るように指定します. これには, CモードやLispモードになったバッファでは, 変数をバッファローカルにしてから, そのモード用の新たな値を変数に設定するのです. See section メジャーモード.

バッファローカルな束縛を作る普通の方法は, 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   ; 消えているべきローカルなletの値が
               ;   バッファ‘a’の現在のバッファローカルな値

しかし, つぎに示すようにsave-excursionを使えば, この問題を回避できます.

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

body内でのfooへの参照は, バッファ‘b’のバッファローカルな束縛を使います.

ファイルでローカル変数の値を指定していると, そのファイルを訪問したときに, それらはバッファローカルな値になります. See (emacs-ja)File Variables section ‘ファイルにローカルな変数’ in GNU Emacs マニュアル.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

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

コマンド: make-local-variable variable

この関数は, カレントバッファにおいて, variable(シンボル)のバッファローカルな束縛を作る. 他のバッファは影響されない. 返す値はvariable.

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 section 複数ディスプレイ.

注意: フック変数に対してmake-local-variableを使わないこと. そのかわりにmake-local-hookを使う. see section フック.

コマンド: make-variable-buffer-local variable

この関数は, variable(シンボル)を 自動的にバッファローカルにするように印を付け, これ以降にその変数に設定しようとすると, その時点のカレントバッファにローカルにする.

この機能の重要な点は, (letや他の束縛を作る構文で)変数を束縛しても, その変数のバッファローカルな束縛を作らないことである. (setsetqで)変数を設定して初めてそのようにする.

返す値はvariableである.

警告: ユーザーが異なるバッファでは異なったカスタマイズをするかもしれないと いうだけで, ユーザーオプション変数にmake-variable-buffer-localを 使うべきだと仮定しないこと. ユーザーは, 必要ならば, どんな変数でもローカルにできる. 選択はユーザーに任せるのがよい.

2つのバッファが同じ束縛を共有しないことが重要な場面では, make-variable-buffer-localを使う. たとえば, 異なるバッファでは異なる値を持つことに依存するような Lispプログラムで内部目的に変数を使うときには, make-variable-buffer-localを使うのが最良である.

Function: local-variable-p variable &optional buffer

これは, variableがバッファbuffer (デフォルトはカレントバッファ)において バッファローカルであればtを返し, さもなければnilを返す.

Function: buffer-local-variables &optional buffer

この関数は, バッファbufferのバッファローカルな変数を 記述したリストを返す. (bufferを省略するとカレントバッファを使う. ) バッファローカルな変数とその値を入れた要素から成る連想リスト (see section 連想リスト)を返す. しかし, 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")
    …
    ;; 続いて, 組み込みでないバッファローカルな変数 
    ;; これはバッファローカルで, かつ, 空
    foobar
    ;; これはバッファローカルで, かつ, 空ではない
    (bind-me . 69))

このリストのコンスセルのCDRに新たな値を格納しても, 変数のバッファローカルな値を変更しないことに注意してほしい.

コマンド: kill-local-variable variable

この関数は, カレントバッファにおけるvariable(シンボル)の バッファローカルな束縛を(あれば)削除する. その結果, このバッファでは, variableのデフォルトの束縛が 見えるようになる. 典型的には, variableの値が変わる. なぜなら, デフォルト値は, 削除したバッファローカルな値とは 普通は異なるからである.

自動的にバッファローカルにする印が付いた変数のバッファローカルな束縛を 削除すると, カレントバッファではデフォルト値が見えるようになる. しかし, 変数に再度設定すると, それに対するバッファローカルな束縛が 再度作成される.

kill-local-variablevariableを返す.

この関数がコマンドであるのは, 対話的にバッファローカルな変数を作るのが有用なように, 対話的にバッファローカルな変数を削除するのが有用な場合があるからである.

Function: kill-all-local-variables

この関数は, カレントバッファにおいて, 『恒久的』と印付けしてある変数を除いて, すべてのバッファローカルな変数束縛を削除する. その結果, バッファでは, ほとんどの変数のデフォルト値が見えるようになる.

この関数は, バッファに属する他のある種の情報もリセットする. つまり, ローカルキーマップにnil, 構文テーブルに(standard-syntax-table)の値, 大文字小文字テーブルに(standard-case-table), 略語テーブルにfundamental-mode-abbrev-tableの値を設定する.

この関数が最初に行うことは, ノーマルフックchange-major-mode-hook(下記参照)を 実行することである.

各メジャーモードコマンドはこの関数を呼び出すことから始める. つまり, 基本(fundamental)モードに切り替え, それ以前のメジャーモードのほとんどの効果を消しさる. この処理を保証するために, メジャーモードで設定する変数には, 恒久的の印を付けないこと.

kill-all-local-variablesnilを返す.

Variable: change-major-mode-hook

関数kill-all-local-variablesは, 最初にこのノーマルフックを実行する. このフックはメジャーモードに対して, ユーザーが別のメジャーモードに切り替えていた場合には, なにか特別なことを行う情報を提供する. 最良の結果を得るためには, この変数をバッファローカルにしておくと その役目を終えると変数は消えてしまい, それ以降のメジャーモードに干渉しない. see section フック.

バッファローカル変数は, 変数名(シンボル)の属性permanent-localnil以外であると, 恒久的(permanent)です. 恒久的なローカル変数は, 編集作業の文脈ではなく, どのファイルを訪問中であるとかどのように保存するとかに関連する情報に 適しています.


[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

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

バッファローカルな束縛がある変数のグローバル値を, デフォルト値とも呼びます. カレントバッファや選択したフレームに変数の独自の束縛がない場合に, グローバル値を使うからです.

関数default-valueと関数setq-defaultは, カレントバッファにバッファローカルな束縛があるかどうかに関わらず, 変数のデフォルト値を参照したり変更したりします. たとえば, setq-defaultを使って, ほとんどのバッファのparagraph-startのデフォルト値を変更できます. この変数のバッファローカルな値があるCモードやLispモードのバッファで 行ってもこれは動作します.

スペシャルフォームdefvardefconstも, バッファローカルやフレームローカルな値ではなく, (変数に設定する場合には)デフォルト値を設定します.

Function: default-value symbol

この関数は, symbolのデフォルト値を返す. この値は, この変数に対して独自の値を持たないバッファやフレームで見える 値である. symbolがバッファローカルでなければ, これは, symbol-value(see section 変数値の参照)と等価.

Function: default-boundp symbol

関数default-boundpは, symbolのデフォルト値が空でないことを調べる. (default-boundp 'foo)nilを返せば, (default-value 'foo)はエラーになる.

default-boundpは, boundpsymbol-valueに対応するように, default-valueに対応する.

Special Form: setq-default [symbol form]…

このスペシャルフォームは, 各symbolに, 対応するformの評価結果である新たなデフォルト値を与える. symbolは評価しないが, formは評価する. フォームsetq-defaultの値は, 最後のformの値である.

symbolがカレントバッファでバッファローカルではなく, かつ, 自動的にバッファローカルにする印が付いていなければ, setq-defaultsetqと同じ効果がある. 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
Function: set-default symbol value

この関数はsetq-defaultに似ているが, symbolは普通どおりに評価される引数である.

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

[ < ] [ > ]   [ << ] [] [ >> ]         [冒頭] [目次] [見出し] [ ? ]

10.11 フレームローカルな変数

変数にバッファローカルな束縛があるように, 変数にはフレームローカルな束縛もあります. これらの束縛は1つのフレームに属し, そのフレームを選択しているときに有効になります. フレームローカルな束縛は, 実際にはフレームパラメータです. 特定のフレームでフレームローカルな束縛を作るには modify-frame-parametersを呼び出し, パラメータ名として変数名を指定します.

特定の変数に対するフレームローカルな束縛を有効にするには, 関数make-variable-frame-localを呼び出します.

コマンド: make-variable-frame-local variable

variableに対してフレームローカルな束縛を使うようにする. この関数そのものはvariableに対してフレームローカルな束縛を作成しない. しかし, フレームパラメータとしてvariableの値を持つフレームが すでに存在すれば, その値は自動的にフレームローカルな束縛になる.

変数が端末にローカルであると, この関数はエラーを通知する. そのような変数はフレームローカルな束縛を同時には持てないからである. see section 複数ディスプレイ. Emacsで特別に実装されている少数の変数は(普通) バッファローカルになることができるが, フレームローカルにはならない.

バッファローカルな束縛はフレームローカルな束縛に優先します. 変数fooを考えてみましょう. カレントバッファにfooのバッファローカルな束縛があると, その束縛が有効になります. 選択したフレームにfooのフレームローカルな束縛があると, その束縛が有効になります. さもなければ, fooのデフォルトの束縛が有効になります.

つぎに例を示します. まず, fooの束縛を準備しておきます.

 
(setq f1 (selected-frame))
(make-variable-frame-local 'foo)

;; b1’において, fooのバッファローカルな束縛を作る
(set-buffer (get-buffer-create "b1"))
(make-local-variable 'foo)
(setq foo '(b 1))

;; 新しいフレームでfooのフレームローカルな束縛を作る
;; そのフレームをf2に格納する
(setq f2 (make-frame))
(modify-frame-parameters f2 '((foo . (f 2))))

では, さまざまな文脈で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)


[ << ] [ >> ]           [冒頭] [目次] [見出し] [ ? ]

この文書は新堂 安孝によって2009年9月22日texi2html 1.82を用いて生成されました。