Manchmal muss ich denselben Wert auf mehrere Variablen setzen.
In Python könnte ich tun
f_loc1 = f_loc2 = "/foo/bar"
Aber in Elisp schreibe ich
(setq f_loc1 "/foo/bar" f_loc2 "/foo/bar")
Ich frage mich, ob es einen Weg gibt, dies mit "/foo/bar"
nur einem Mal zu erreichen .
source
&target
auf den gleichen Pfad setzen willst und ich ihntarget
später möglicherweise ändere , wie setze ich sie dann?Antworten:
setq
gibt den Wert zurück, so dass Sie einfach:Wenn Sie sich nicht darauf verlassen möchten, verwenden Sie:
Persönlich würde ich letzteres vermeiden und stattdessen schreiben:
oder auch
Und der allererste Ansatz würde ich nur in ganz besonderen Situationen anwenden, in denen er tatsächlich die Absicht betont , oder wenn die Alternative darin besteht, den zweiten Ansatz zu verwenden, oder sonst
progn
.Sie können hier aufhören zu lesen, wenn das oben Genannte Sie glücklich macht. Aber ich denke, dies ist eine gute Gelegenheit, Sie und andere zu warnen, Makros nicht zu missbrauchen.
Auch wenn es verlockend ist, ein Makro wie
setq-every
"weil das lispelt und wir es können" zu schreiben , würde ich dringend empfehlen, dies nicht zu tun. Sie gewinnen sehr wenig, indem Sie es tun:Gleichzeitig erschweren Sie anderen Lesern das Lesen des Codes und stellen sicher, was passiert.
setq-every
Es kann auf verschiedene Arten implementiert werden, und andere Benutzer (einschließlich einer Zukunft, für die Sie sich entschieden haben) können nicht wissen, für welche sie sich entscheiden. [Ursprünglich habe ich hier einige Beispiele angeführt, aber diese wurden von jemandem als sarkastisch und beschimpft angesehen.]Sie können mit diesem mentalen Overhead jedes Mal umgehen, wenn Sie es sich ansehen,
setq-every
oder Sie können nur mit einem einzigen zusätzlichen Charakter leben. (Zumindest für den Fall, dass Sie nur zwei Variablen setzen müssen, aber ich kann mich nicht erinnern, dass ich jemals mehr als zwei Variablen auf einmal auf den gleichen Wert setzen musste. Wenn Sie das tun müssen, ist wahrscheinlich etwas anderes falsch mit deinem Code.)Wenn Sie dieses Makro jedoch tatsächlich mehr als ein halbes Dutzend Mal verwenden, ist die zusätzliche Indirektion möglicherweise die Mühe wert. Zum Beispiel verwende ich Makros wie
--when-let
aus derdash.el
Bibliothek. Das macht es denjenigen schwerer, die zum ersten Mal in meinem Code darauf stoßen, aber es lohnt sich, diese Verständnisschwierigkeiten zu überwinden, da der Code mehr als nur ein paar Mal verwendet wird und zumindest meiner Meinung nach besser lesbar ist .Man könnte argumentieren, dass das Gleiche gilt
setq-every
, aber ich denke, dass es sehr unwahrscheinlich ist, dass dieses spezielle Makro mehr als ein paar Mal verwendet wird. Eine gute Faustregel ist, dass, wenn die Definition des Makros (einschließlich der doc-Zeichenfolge) mehr Code hinzufügt als die Verwendung des Makros entfernt, das Makro sehr wahrscheinlich überfordert ist (das Gegenteil ist jedoch nicht unbedingt der Fall).quelle
setq
haben, können Sie auf ihren neuen Wert verweisen, sodass Sie auch diese Monstrosität verwenden können:(setq a 10 b a c a d a e a)
abcd und e werden auf 10 gesetzt.Ein Makro, um zu tun, was Sie wollen
Als eine Art Übung:
Probieren Sie es jetzt aus:
Wie funktioniert es
Da die Leute neugierig sind, wie es funktioniert (laut Kommentaren), folgt hier eine Erklärung. Um wirklich zu lernen, wie man Makros schreibt, wählen Sie ein gutes Common Lisp-Buch (ja, Common Lisp, Sie werden in Emacs Lisp dasselbe tun können, aber Common Lisp ist ein bisschen leistungsfähiger und hat bessere Bücher, IMHO).
Makros arbeiten mit Rohcode. Makros werten ihre Argumente nicht aus (im Gegensatz zu Funktionen). Wir haben also hier unbewertet
value
und eine Sammlung vonvars
, die für unser Makro nur Symbole sind.progn
gruppiert mehreresetq
Formen zu einer. Dieses Ding:Erzeugt einfach eine Liste von
setq
Formularen, am Beispiel von OP:Sie sehen, das Formular befindet sich innerhalb des Backquote-Formulars und wird mit einem Komma vorangestellt
,
. Innerhalb des Backquoted-Formulars wird alles wie gewohnt angegeben, die,
Auswertung wird jedoch vorübergehend "eingeschaltet", sodassmapcar
das Ganze zur Makroexpansionszeit ausgewertet wird.@
Entfernt schließlich die äußere Klammer von der Liste mitsetq
s, so erhalten wir:Makros können Ihren Quellcode beliebig verändern, ist das nicht großartig?
Eine Einschränkung
Hier ist eine kleine Einschränkung, das erste Argument wird mehrmals ausgewertet, da dieses Makro im Wesentlichen wie folgt erweitert wird:
Sie sehen, wenn Sie eine Variable oder einen String hier haben, ist es in Ordnung, aber wenn Sie so etwas schreiben:
Dann wird Ihre Funktion mehrmals aufgerufen. Dies kann unerwünscht sein. So beheben Sie das Problem mithilfe von
once-only
(im MMT-Paket verfügbar ):Und das Problem ist weg.
quelle
value
Makro-Expansionszeit auswerten .(macroexpand '(setq-every user-emacs-directory f-loc1 f-loc2))
->(progn (setq f-loc1 user-emacs-directory) (setq f-loc2 user-emacs-directory))
. Wenn sievalue
zur Makroexpansionszeit ausgewertet werden, wird sie durch die tatsächliche Zeichenfolge ersetzt. Sie können einen Funktionsaufruf mit Nebenwirkungen verwenden,value
stattdessen wird er erst ausgewertet, wenn erweiterter Code ausgeführt wird. Probieren Sie es aus.value
als Argument von Makro wird nicht automatisch ausgewertet. Das eigentliche Problem ist, dassvalue
mehrmals ausgewertet wird, was möglicherweise unerwünscht ist,once-only
kann hier Abhilfe schaffen.mmt-once-only
(was eine externe Dep erfordert) könnten Sie verwendenmacroexp-let2
.set
, nicht nachsetq
Makros. Oder einfachsetq
mit mehreren Argumenten verwenden, einschließlich der Wiederholung von Argumenten.Da Sie Symbole in lisp verwenden und bearbeiten können, können Sie einfach eine Liste von Symbolen durchlaufen und verwenden
set
.quelle