Help us understand the problem. What is going on with this article?

バッファローカル変数の挙動

More than 3 years have passed since last update.

バッファローカルとは

  • Emacs Lispのすべての変数は「バッファローカル」というフラグを持っている
  • このフラグはバッファごとに設定されている。たとえば、「変数xをバッファ*scratch*においてバッファローカルにする」といった操作ができる
  • 変数は、何も設定しなければ、どのバッファにおいても、バッファローカルではない

バッファローカル変数の性質

  • 変数には「バッファごとの値」と「デフォルト値」が設定できる
  • (現在のバッファにおいて)バッファローカルな変数の読み出し variable-name ・セット (setq variable-name 1) は、「バッファごとの値」に対して行われる
  • (現在のバッファにおいて)バッファローカルでない変数の読み出し・セットは、「デフォルト値」に対して行われる

local-variable-p

変数xが(現在のバッファにおいて)バッファローカルかどうかは、
(local-variable-p 'x)
で確認できる。

make-local-variablekill-local-variable

変数xを現在のバッファにおいてバッファローカルにするには、
(make-local-variable 'x)
を評価する。

変数xを現在のバッファにおいてバッファローカルでなくするには、
(kill-local-variable 'x)
を評価する。

default-valuesetq-default

(default-value 'x)
を評価すると、xがバッファローカルであるかどうかに関わらず、xのデフォルト値が読み出される。

(setq-default x 1)
を評価すると、xがバッファローカルであるかどうかに関わらず、xのデフォルト値が1にセットされる。

この例は、新たに作成したバッファ上で評価することを推奨する。また、評価にはlispxmp.elを用いることを推奨する。

(setq x 'default-value-1)
(make-local-variable 'x)
(setq x 'buffer-local-value)

(local-variable-p 'x)  ; => t
x                      ; => buffer-local-value

(with-temp-buffer  ; 一時バッファ上で評価
  (local-variable-p 'x)      ; => nil
  x                          ; => default-value-1

  (setq x 'default-value-2)
  x                          ; => default-value-2
  )

x                   ; => buffer-local-value
(default-value 'x)  ; => default-value-2

(setq-default x 'default-value-3)
x                   ; => buffer-local-value
(default-value 'x)  ; => default-value-3

(kill-local-variable 'x)
(local-variable-p 'x)  ; => nil
x                      ; => default-value-3

バッファローカルとlet

Emacs Lispでは、letフォームなどによって、ローカルな変数を作ることができる。「バッファローカル」と「ローカル」は全く異なる概念である。

例1

この例は、新たに作成したバッファ上で評価することを推奨する。
現在のバッファ名をCurrent Bufferとする。

(setq x 'default-value-1)
(make-local-variable 'x)
(setq x 'buffer-local-value)

(local-variable-p 'x)  ; => t
x                      ; => buffer-local-value

(let ((x 'buffer-local-value-in-let))
  (local-variable-p 'x)  ; => t
  x                      ; => buffer-local-value-in-let

  (with-temp-buffer
    (local-variable-p 'x)  ; => nil
    x                      ; => default-value-1

    (setq x 'default-value-2)
    x                      ; => default-value-2
    )

  x                   ; => buffer-local-value-in-let
  (default-value 'x)  ; => default-value-2
  )

x  ; => buffer-local-value

with-temp-buffer内でのxの読み書きは、デフォルト値に対して行われる。したがって、'buffer-local-value-in-letを読み出すことはできない。また、with-temp-buffer内でxの値をセットしても、xCurrent Bufferにおいてバッファローカルであるため、値は変化しない。ただし、デフォルト値は変更されているので、(default-value 'x)によってその値を読み出すことができる。

例2

この例は、新たに作成したバッファ上で評価することを推奨する。
現在のバッファ名をCurrent Bufferとする。

(setq x 'default-value-1)
;; バッファ "Another Buffer" を作成し、 "Another Buffer" 上で評価
(with-current-buffer (get-buffer-create "Another Buffer")
  (make-local-variable 'x)
  (setq x 'buffer-local-value-1))

(local-variable-p 'x)  ; => nil
x                      ; => default-value-1

(let ((x 'default-value-in-let))
  (local-variable-p 'x)  ; => nil
  x                      ; => default-value-in-let

  ;; バッファ "Another Buffer" 上で評価
  (with-current-buffer "Another Buffer"
    (local-variable-p 'x)  ; => t
    x                      ; => buffer-local-value-1

    (setq x 'buffer-local-value-2)
    x                      ; => buffer-local-value-2
    (default-value 'x)     ; => default-value-in-let

    (setq-default x 'default-value-2))

  x  ; => default-value-2
  )

x  ; => default-value-1

Another Bufferにおいてxはバッファローカルであるため、xの読み書きは、バッファごとの値に対して行われる。Current Bufferでセットした値は、(default-value 'x)によって読み出すことができる。また、setq-defaultによって、Current Bufferで読み出される値を設定することができる。

make-variable-buffer-local

(make-variable-buffer-local 'x)
を評価すると、(setq x 1)などとしてxに値をセットしたとき、xが自動的に(現在のバッファにおいて)バッファローカルになる。この、自動的にバッファローカルにする効果は、すべてのバッファにおいて有効になる。

例1

この例は、Emacsを再起動してから評価することを推奨する。

(setq x1 'default-value)
(make-variable-buffer-local 'x1)

(local-variable-p 'x1)  ; => nil
x1                      ; => default-value

(setq x1 'buffer-local-value)
(local-variable-p 'x1)  ; => t
x1                      ; => buffer-local-value

(make-variable-buffer-local 'x1)を評価した後に、x1の値をセットすると、x1が自動的にバッファローカルになっている。

例2

この例は、Emacsを再起動してから評価することを推奨する。

(setq x2 'default-value)
(make-variable-buffer-local 'x2)

(with-temp-buffer
  (local-variable-p 'x2)  ; => nil
  x2                      ; => default-value

  (setq x2 'buffer-local-value)
  (local-variable-p 'x2)  ; => t
  x2                      ; => buffer-local-value
  )

(local-variable-p 'x2)  ; => nil
x2                      ; => default-value

(make-variable-buffer-local 'x2)を評価したバッファとは、異なるバッファの上でx2に値をセットしても、x2は(移ったバッファにおいて)バッファローカルになる。

letによるシャドウイング

(make-variable-buffer-local 'x)を評価したのち、letなどでxをシャドウした上で値をセットしても、xはバッファローカルにならない。

例3

この例は、Emacsを再起動してから評価することを推奨する。

(setq x3 'default-value)
(make-variable-buffer-local 'x3)

(let ((x3 'default-value-in-let-1))
  (local-variable-p 'x3)  ; => nil
  x3                      ; => default-value-in-let-1

  (setq x3 'default-value-in-let-2)
  (local-variable-p 'x3)  ; => nil
  x3                      ; => default-value-in-let-2
  )

(make-variable-buffer-local 'x3)を評価したあとで、letによってx3に値をセットしているが、x3はバッファローカルにならない。また、シャドウした後のx3に値をセットしても、x3はバッファローカルにならない。

例4

この例は、Emacsを再起動してから評価することを推奨する。

(let ((x4 'default-value-in-let))
  (make-variable-buffer-local 'x4)
  (setq x4 'buffer-local-value))

(local-variable-p 'x4)  ; => t
x4                      ; => buffer-local-value

ローカルな変数にmake-variable-buffer-localを適用しても、変数をシャドウせずにセットすれば、バッファローカルになる。ただし、これは邪悪なコードらしく、評価すると

Making x4 buffer-local while let-bound!

という警告がエコーエリアに表示される。


注意すべきは、
(make-variable-buffer-local 'x)
を評価した時点では、xはバッファローカルにならないことである。make-variable-buffer-local(変数をバッファローカルにする)という名前は、この点で誤解しやすい。そのため、筆者はinit.elで
(defalias 'make-variable-autolocal 'make-variable-buffer-local)
と設定している。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away