バッファローカル変数のデフォルト値は最初の `setq`まで設定されません


7

私がバッファローカル変数を定義し、fooそのデフォルト値が "a"であるとしましょう:

(defvar foo "a")
(make-variable-buffer-local 'foo)
(default-value 'foo) ;; => "a"

この直後に、次のコードを実行します。

(let ((foo "b"))
  (with-temp-buffer
    (message "foo: %s" foo))) ;; => "b"

結果は "b"です。これは、で設定した値letです。

setq変数の設定に使用する場合は、前とまったく同じコードを再実行します。

(setq foo "c") ;; => "c"

(let ((foo "b"))
  (with-temp-buffer
    (message "foo: %s" foo))) ;; => "a"

結果は "a"で、これが現在のデフォルト値です。

質問:一時バッファの場合、のデフォルト値はfoo使用するまで設定されませんsetqか?そしてsetq、私が使用しない限りlet、他のバッファのデフォルト値を変更するために使用できますか?

編集:@npostavsが言ったように、これはmake-varible-buffer-local本当に意味することです。make-variable-buffer-local自分で使えば、setqその後いつでも使えます。しかし、これはのような「組み込み」のバッファローカル変数では本当にトリッキーになりますcase-fold-search。私がパッケージ作成者として外部でバインドcase-fold-searchし、でデフォルト値(ユーザーが設定したかどうかにかかわらず)を使用したい場合、デフォルト値が実際にあることを確認する前に使用する必要があります場合に使用されているユーザはそれを持っていない、彼/彼女に。バッファローカル変数の場合、おそらく、値を設定するときよりも常に安全であることを意味します。デザインやドキュメンテーションは改善できるのかしら。nilletwith-temp-buffersetqwith-temp-buffersetqinit.elsetqlet


縛られた部分をwith-temp-buffer(前ではなく)の体の中に入れた場合、それは役に立ちますか? with-temp-bufferマクロであり、標準の関数とは少し異なる動作をします。例:(with-temp-buffer (let ((foo "b")) (message "foo: %s" foo)))
法律家

@lawlistそれは私が理解している期待通りに機能するはずです。しかし、質問で言った例を説明するドキュメントは見つかりません。
cutejumper 2017

私はおそらく質問をするでしょう-以前にバインドされた変数でマクロの本体部分をどのように貫通できますか(たとえば、バックティック/コンマアプローチ、または字句レット、またはおそらく何か他のもので?)...しかし、私はあなたの質問を乗っ取りたくありません...マクロmavenがすぐにすべてを説明するために一緒にいる必要があります... :)
法律家

1
@lawlistこれがマクロに関連しているかどうかはわかりません。私は実際にwith-temp-buffer(つまり、マクロなし)の拡張形式を使用してローカルでテストします。バッファローカル変数の特定の動作に似ていると思います。
cutejumper 2017

回答:


6

私はパッケージ作成者として、外側のletにバインドcase-fold-searchnil、でデフォルト値(ユーザーが設定したかどうかにかかわらず)を使用したいと思いますwith-temp-buffer

この場合、私はお勧めします

(let ((case-fold-search (progn (make-local-variable 'case-fold-search)
                               nil)))
  (with-temp-buffer
    ...))

ここで注意すべき重要なことはmake-variable-buffer-local、変数がバッファローカルにならないことです!設定された後にバッファローカルになるように調整するだけです。make-local-variable代わりに使用することもできます。

(make-variable-buffer-local VARIABLE)

Make VARIABLE become buffer-local whenever it is set.

次に注意する点は、letバッファローカル変数との相互作用です(elisp) Intro to Buffer-Local

警告:変数が1つ以上のバッファーにバッファーローカルバインディングを持っている場合、let現在有効なバインディングを再バインドします。たとえば、現在のバッファーにバッファーローカル値がある場合、let 一時的にそれを再バインドします。有効なバッファーローカルバインディングがない場合は let、デフォルト値を再バインドします。

したがって、最初のケースでは、グローバル値バインド"b"、それが一時バッファーに表示されます。あなたがローカル値を設定した後に、第2のケースでは、*scratch*"c"、あなたはその後、地元の値をバインドする"b"が、他のバッファがまだのグローバルな値を参照してください"a"


私はその文を誤解したと思いMake VARIABLE become buffer-local whenever it is set.ます。値を設定するときは常に、バッファローカルな値のみが設定されると言っていたと思いました。したがって、このような問題を回避するsetqには、常にmake-variable-buffer-local?の直後に常に使用する必要があります。
cutejumper 2017

実際に、case-fold-searchやなどの「組み込み」バッファローカル変数でこの問題が発生しますfill-column。Emacs自体は、これらのバッファーローカル変数を定義した後は設定しません。実際にバッファーローカルにするためには、ユーザーが設定する必要があります。このデザインは意図的なものですか?ほとんどの人はこれを知っているとは思いません。
cutejumper 2017

ユーザーに使用を要求するmake-local-variablesetq、この変数がバッファーローカルであると表示している間は、本当に混乱します。しかし、多分それはデザインに関する別の質問です。
cutejumper 2017

さて、それがすぐにバッファローカルになった場合、問題はどのようにデフォルト値をバインドするかです...
npostavs

ありがとう。make-local-variable変数がマニュアルからすでに「バッファローカル」であることを確認したとき、私はまだ使用するのがおかしいと感じています。letバッファローカルコピーをバインドできると思います。しかし、これはEmacs-develで尋ねるのにより適しているかもしれません。
cutejumper 2017

0

提案されたソリューションと同様に、これを内の任意の変数にバインドし、内letにとどまっている間に必要に応じて変更しletてから、元の値に戻すことができます。

内部でバッファを変更するときに問題が発生しletます-動的スコープのため、ローカル変数の値は、内部にある場合でも、新しいバッファに「持ち越され」ませんlexical-let

この問題をチェックし、一時バッファで使用されるデフォルト値を設定して元に戻す関数を次に示します。

(defun f1 (VAR TEMP-VALUE) "
Sets VAR to TEMP-VALUE; works with local variables also." 
   (interactive)
   ;; starting value
   (insert (prin1-to-string VAR))
   (let ((var1 VAR))
     (setq VAR TEMP-VALUE)
     (when (local-variable-p 'VAR)
       (setq-default VAR TEMP-VALUE))
     ;; current buffer-local value
     (insert (prin1-to-string VAR))
     ;; value in new buffer
     (with-temp-buffer
       (message (prin1-to-string VAR)))
     ;; back to current buffer - unchanged
     (insert (prin1-to-string VAR))
     ;; revert
     (setq VAR var1)
     (when (local-variable-p 'VAR)
       (setq-default VAR var1))
   ;; back to original value
   (insert (prin1-to-string VAR)))
   ;; exit let and re-check
   (insert (prin1-to-string VAR)))

その後

(f1 case-fold-search "xyz") --> t"xyz""xyz"tt

「xyz」が*Messages*バッファに表示されます。

このコンテキストで頭に浮かぶ他のローカル変数はdefault-directory、そしてuniquify-managed...

簡単に言うと、このような関数を定義して内letで使用できるので、変数(シンボル)がローカルかどうかを確認する手間を省くことができます。

(defun set-ld (SYM VAL) "set local (and default if applicable)"
       (interactive)
       (setq SYM VAL)
       (when (local-variable-p 'SYM)
     (setq-default SYM VAL)))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.