Der Standardwert der pufferlokalen Variablen wird erst mit dem ersten `setq` festgelegt

7

Angenommen, ich definiere eine pufferlokale Variable foound ihr Standardwert ist "a":

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

Unmittelbar danach führe ich den folgenden Code aus:

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

Das Ergebnis ist "b", was der Wert ist, den ich eingestellt habe let.

Wenn ich setqnun die Variable setze, führe genau den gleichen Code wie zuvor erneut aus:

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

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

Das Ergebnis ist "a", was jetzt der Standardwert ist.

Die Frage : Für einen temporären Puffer wird der Standardwert von fooerst festgelegt, wenn ich ihn verwende setq. Und solange ich nicht verwende setq, kann ich letden Standardwert in anderen Puffern ändern?

EDIT : Wie @npostavs sagte, ist dies das, was make-varible-buffer-localwirklich bedeutet. Wenn ich make-variable-buffer-localmich selbst benutze , kann ich danach immer noch benutzen setq. Dies wird jedoch sehr schwierig für die "eingebauten" pufferlokalen Variablen wie case-fold-search. wenn ich als Paketautor, bindet case-fold-searchan nilin den äußeren let, und ich mag den Standardwert verwenden , in der (es kann oder auch nicht vom Benutzer eingestellt werden) with-temp-buffer, muß ich verwenden , setqbevor with-temp-bufferSie sicher , dass der Standardwert tatsächlich zu machen ist Wird verwendet, falls der Benutzer dies nicht setqin sich hat init.el. Für pufferlokale Variablen bedeutet dies wahrscheinlich, dass setqes immer sicherer ist, als letwenn wir den Wert festlegen möchten. Ich frage mich, ob das Design oder die Dokumentation verbessert werden könnten.

süßer Pullover
quelle
Wenn Sie den let-gebundenen Teil in den Körper von with-temp-buffer(anstatt davor) legen , hilft das irgendetwas? with-temp-bufferist ein Makro und verhält sich etwas anders als eine Standardfunktion. ZB(with-temp-buffer (let ((foo "b")) (message "foo: %s" foo)))
Lawlist
@lawlist Das sollte wie erwartet funktionieren, was ich verstehe. Ich kann jedoch keine Dokumentation finden, die das Beispiel erklärt, das ich in der Frage angegeben habe.
cutejumper
Ich würde wahrscheinlich die Frage stellen: Wie kann ich mit einer zuvor let-gebundenen Variablen (z. B. mit einem Back-Tick / Komma-Ansatz oder einem lexikalischen Let oder vielleicht etwas anderem) in den Body-Teil eines Makros eindringen? ... Aber ich möchte Ihre Frage nicht entführen ... ein Makro-Maven sollte in Kürze da sein, um alles zu erklären ... :)
Lawlist
1
@lawlist Ich weiß nicht, ob dies mit dem Makro zusammenhängt. Ich teste es tatsächlich lokal mit der erweiterten Form von with-temp-buffer(das heißt, keine Makros). Ich denke, es ist eher ein spezifisches Verhalten für pufferlokale Variablen.
cutejumper

Antworten:

6

Ich bin als Paketautor case-fold-searchan nildas äußere let gebunden und möchte den Standardwert (der vom Benutzer festgelegt werden kann oder nicht) im with-temp-buffer, verwenden.

In diesem Fall würde ich empfehlen

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

Das Wichtigste dabei ist, dass make-variable-buffer-localeine Variable nicht pufferlokal ist! Es sorgt erst dafür, dass es nach dem Festlegen pufferlokal wird . Vielleicht möchten Sie make-local-variablestattdessen verwenden.

(make-variable-buffer-local VARIABLE)

Make VARIABLE become buffer-local whenever it is set.

Das zweite, was zu beachten ist, ist die Interaktion letmit pufferlokalen Variablen von (elisp) Intro to Buffer-Local:

Warnung: Wenn eine Variable letpufferlokale Bindungen in einem oder mehreren Puffern enthält, wird die aktuell gültige Bindung erneut gebunden. Wenn der aktuelle Puffer beispielsweise einen let pufferlokalen Wert hat, wird dieser vorübergehend neu gebunden. Wenn keine letpufferlokalen Bindungen wirksam sind, wird der Standardwert erneut gebunden.

Im ersten Fall binden Sie also den globalen Wert an "b"und dieser wird im temporären Puffer angezeigt. Im zweiten Fall, nachdem Sie den lokalen Wert *scratch*auf festgelegt haben "c", binden Sie den lokalen Wert an "b", aber andere Puffer sehen immer noch den globalen Wert von "a".

npostavs
quelle
Ich glaube, ich habe den Satz falsch verstanden Make VARIABLE become buffer-local whenever it is set.. Ich dachte, es heißt, wann immer wir den Wert setzen, wird nur der pufferlokale Wert gesetzt. Um ein solches Problem zu vermeiden, sollten wir es wahrscheinlich immer setqsofort danach verwenden make-variable-buffer-local?
cutejumper
Ich stoße tatsächlich auf dieses Problem für "eingebaute" pufferlokale Variablen wie case-fold-searchund fill-column. Emacs selbst setzt sie nicht, nachdem es diese pufferlokalen Variablen definiert hat, und der Benutzer muss sie setzen, damit sie wirklich pufferlokal sind. Ist dieses Design beabsichtigt? Ich glaube nicht, dass die meisten Leute sich dessen bewusst sind.
cutejumper
Es ist wirklich verwirrend, wenn der Benutzer diese Variable verwenden muss make-local-variableoder setqwenn er sagt, dass diese Variable pufferlokal ist. Aber vielleicht ist das eine andere Frage bezüglich des Designs.
cutejumper
Nun, wenn es sofort lokal
gepuffert
Vielen Dank. Ich finde es immer noch komisch, make-local-variablewenn ich sehe, dass die Variable bereits "buffer-local" aus dem Handbuch ist. Ich würde erwarten, dass das letdie pufferlokale Kopie binden könnte. Dies ist jedoch möglicherweise besser geeignet, um in Emacs-devel gefragt zu werden.
cutejumper
0

Wie bei Ihrer vorgeschlagenen Lösung können wir diese an eine beliebige Variable im Inneren binden und dann nach Belieben letändern, während wir im Inneren bleiben let, bevor wir zum ursprünglichen Wert zurückkehren.

Das Problem tritt beim Ändern des Puffers im Inneren auf. letAufgrund des dynamischen Bereichs wird der Wert lokaler Variablen auch im Inneren nicht in den neuen Puffer übertragen lexical-let.

Hier ist eine Funktion, die nach diesem Problem sucht und auch den Standardwert festlegt und zurücksetzt, der von einem temporären Puffer verwendet wird:

(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)))

dann

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

und "xyz" erscheinen im *Messages*Puffer.

Andere lokale Variablen, die in diesem Zusammenhang in den Sinn kommen, sind default-directoryund uniquify-managed...

Kurz gesagt, Sie können eine Funktion wie diese definieren und im Inneren verwenden let. Sparen Sie sich also den Aufwand, zu überprüfen, ob die Variable (Symbol) lokal ist oder nicht:

(defun set-ld (SYM VAL) "set local (and default if applicable)"
       (interactive)
       (setq SYM VAL)
       (when (local-variable-p 'SYM)
     (setq-default SYM VAL)))
Dardisco
quelle