Was macht `setq-local` und wann soll ich es verwenden?

16

Ich bin mir nicht ganz sicher, welche Variationen von pufferlokalen Variablen es gibt, selbst nachdem ich alle Dokumente und eine Reihe von Beiträgen hier auf SX gelesen habe.

Hier ist eine Zusammenfassung meines Verständnisses:

(defvar foo ..)deklariert eine dynamische Variable für die Datei. Die Variable ist jedoch (1) anderen Dateien nur bekannt, wenn sie auch eine defvar Anweisung enthält, und (2) der Gültigkeitsbereich der Variablen ist global und nicht lokal gepuffert.

(make-variable-buffer-local foo)nachdem das defvaroben Gesagte dem Compiler und allen anderen mitgeteilt hat, dass die Variable foo überall dort als pufferlokal zu behandeln ist, wo sie gesetzt ist, wenn sie gesetzt ist. Dieses Muster eignet sich also gut zum Deklarieren einer pufferlokalen Variablen, bei der beide Anweisungen in der Datei hintereinander abgelegt werden.

(defvar xxx ...)                    ;declare xxx with global scope
(make-variable-buffer-local 'xxx)   ;but now make it buffer-local everywhere

Der Einfachheit halber kann das (defvar-local xxx ...)Formular anstelle der beiden obigen Zeilen als eine Zeile verwendet werden:

(defvar-local xxx ...)       ;make xxx buffer local everywhere

Einmal wie oben deklariert, kann die Variable xxx wie jede andere Variable in setq-Anweisungen verwendet werden.

Wenn ich nur eine einzelne Instanz einer pufferlokalen Variablen haben möchte, die bereits eine globale dynamische Variable ist, würde ich die folgenden Deklarationen verwenden. Die erste deklariert die dynamische Variable mit globalem Gültigkeitsbereich und die zweite Anweisung erstellt nur eine Instanz einer pufferlokalen Version dieser Variablen im aktuellen Puffer:

(defvar xxx ...)             ;declare xxx with global scope
(make-local-variable 'xxx)   ;make xxx local in this buffer only

Nun zu meinen expliziten Fragen (alle oben genannten waren implizite Fragen, ob mein Verständnis korrekt ist).

Wenn ich den Wert von Variablen einstelle, kann ich setqoder verwenden setq-local. Wann sollte setq-localverwendet werden? Warum?

Was passiert, wenn ich setq-localpufferlokale oder nicht pufferlokale Variablen verwende?

Wird setq-localfür eine defvar-localdeklarierte Variable benötigt?

Verwandelt sich setq-localeine normal defvardeklarierte Variable in eine pufferlokale Variable? (Mit anderen Worten, ist das setq-localirgendwie das Äquivalent einer (make-variable-local xxx)Erklärung?

Kevin
quelle
Vielen Dank für die zusätzliche Arbeit, Scott. Ich werde die zusätzlichen Backquotes von hier an in einfügen.
Kevin
2
(setq-local VAR VALUE)ist nur eine Abkürzung für (set (make-local-variable VAR) VALUE), was eine verbreitete Redewendung war (und immer noch ist).
Drew

Antworten:

18

Die meisten Ihrer Annahmen sind nah. Ich werde später einige erwähnen. Aber zuerst die Hauptfrage.

Das Formular setq-localist lediglich eine Annehmlichkeit, es ist dasselbe wie das Ausführen, make-local-variablegefolgt von setq. Wenn Sie sich C-h f setq-localdie Dokumentation angesehen und zur Quelle durchgeklickt haben, haben Sie dies möglicherweise gesehen. So habe ich meinen ersten Eindruck bestätigt. Der Code ist jedoch etwas unklar, da er Dinge wie die Tatsache optimiert, dass make-local-variabledie Variable selbst zurückgegeben wird. Mithilfe von setq-localwird die Lokalität in einigen Codes hervorgehoben, sodass andere wissen, dass der Code nicht mit dem globalen Wert der Variablen abgespielt werden kann. Sie müssen es nicht verwenden, um auf lokale Variablen zuzugreifen. Jede Variable kann pufferlokal gemacht werden und wirkt sich auf den gesamten Code aus, der die Variable berührt (es sei denn, es wird alles unternommen, um die globale Kopie mit setq-defaultoder ähnlichem zu erhalten).

Das ist die primäre Antwort. Nun, es gibt einige Dinge in Ihrem Hintergrund, die ein wenig anders sind. Da Sie danach gefragt haben, werde ich einige Dinge ansprechen.

Die erste ist "anderen Dateien nicht bekannt". Das stimmt nicht wirklich. Jeder Code kann auf jede globale Variable verweisen (deshalb werden sie "global" genannt), ohne das defvar(und C-h f defvarsagt es). Tatsächlich sollte es für jede globale Variable nur ein Hauptverzeichnis defvar(mit Dokumentzeichenfolge und Standardwert) geben. Ein defvarmit nur dem Variablennamen kann verwendet werden, um eine Byte-Compiler-Warnung zu unterdrücken. Emacs verwendet nur den Wert des ersten, den es sieht¹, und den Docstring des letzten. Wenn es mehrere defvars mit value / docstring gibt, können sie für die Leser des Codes verwirrend sein. Und die Reihenfolge des Ladens von Dateien spielt eine Rolle.

Einige Variablen werden soll nur als Puffer-local verwendet werden und das sind diejenigen , die Verwendung defvar-local(oder die zweiteilige defvar/ make-variable-buffer-localdie es kurz für ist, vor allem bei älteren Code , bevor die Kurzform hinzugefügt wurde). Dies macht es in allen Puffern pufferlokal. Für einige Variablen ist ihre primäre Verwendung nicht pufferlokal, aber aus irgendeinem Grund möchten Sie möglicherweise, dass ein Puffer einen anderen Wert hat. Das ist, wenn Sie verwenden make-local-variable, in der Regel in einigen Puffer-Setup-Code fast nie direkt nach einem defvar.

Und im Allgemeinen haben die defvarFormen zwei Zwecke . Eine besteht darin, eine Dokumentation zu haben, damit C-h vandere wissen, wozu die Variable dient. Die zweite besteht darin, einen "Standard" -Wert zu deklarieren, damit die Variable immer gesetzt wird. Die Deklaration des Standardwerts wirkt sich nur auf die globale (oder Standard-) Instanz einer Variablen aus und wird nur verwendet, wenn für diese Instanz noch kein Wert festgelegt wurde. Das heißt, wenn Sie setqeine Variable und dann die Datei mit defvar(z. B. durch ein require), wird die Defvar den Wert nicht ändern.

¹ Genauer gesagt: defvarÄndert den Wert der Variablen nicht, wenn er bereits festgelegt ist (es wird jedoch die Dokumentzeichenfolge festgelegt). Dies ist so gemeint, dass die Init-Datei des Benutzers dies tun kann, (setq some-variable)bevor ein Paket geladen wird, um den Standardwert des Pakets zu überschreiben.

KARTE
quelle
1
Vielen Dank für die sehr klare Antwort, es hilft sehr. Als ich den Ausdruck "nicht in anderen Dateien bekannt" verwendete, dachte ich an Warnungen zu freien Variablen, da diese anderen Dateien glauben, dass die Variable frei ist, es sei denn, ich füge ihnen ebenfalls eine defvar hinzu. Ich mag Ihre Aussage, set-localdie den Lesern sagt, dass eine lokale Variable gesetzt wird. Alles, was ich tun kann, um die Lesbarkeit meines Codes zu verbessern, ist gut! PS. Ich werde versuchen, öfter zur Quelle durchzuklicken. Bei meinem derzeitigen Entwicklungsstand kommt es oft nicht wirklich auf die Semantik an ... :-)
Kevin
1
„Es gibt nur eine sein sollte defvarfür jede globale Variable“ ist nicht ganz richtig - es gibt Situationen , in denen (defvar VARNAME) ohne einen Wert sollte unabhängig von der richtigen Definition deklariert werden (mit Anfangswert und im Idealfall eines docstring) davon VARNAME.
Phils
2
Beachten Sie auch, dass setq-localund defvar-localnur in Emacs 24.3 eingeführt wurden.
Phils
1
@phils, könntest du bitte die Situationen identifizieren, von denen du sprichst, wo ohne Wert (defvar varname)deklariert werden sollte ? Ich denke, das hat Drew in einem anderen Thread vorgeschlagen, als ich gefragt habe, wie ich Warnungen zu freien Variablen in meinen Dateien für Globals entfernen kann, die ich an anderer Stelle deklariert hatte. Ich mache das jetzt. Ist das die Situation, von der Sie sprechen?
Kevin
1
Das Vermeiden von Warnungen bei der Bytekompilierung ist ein Grund, ja (siehe C-h i g (elisp) Warning Tips). Die andere ist für Bibliotheken gedacht, die die lexikalische Bindung verwenden, um (falls erforderlich) sicherzustellen, dass eine Variable dynamisch und nicht lexikalisch gebunden ist.
Phils