Parfois, je dois définir la même valeur sur plusieurs variables.
En Python, je pourrais faire
f_loc1 = f_loc2 = "/foo/bar"
Mais en elisp, j'écris
(setq f_loc1 "/foo/bar" f_loc2 "/foo/bar")
Je me demande s'il existe un moyen d'y parvenir en utilisant "/foo/bar"
une seule fois?
source
&target
sur le même chemin au début et que je pourrais changertarget
plus tard, comment les définir ensuite?Réponses:
setq
renvoie la valeur, vous pouvez donc simplement:Si vous ne voulez pas vous y fier, utilisez:
Personnellement, j'éviterais ce dernier et j'écrirais à la place:
ou même
Et la toute première approche que je n'utiliserais que dans des circonstances plutôt spéciales où elle met réellement l' accent sur l'intention, ou lorsque l'alternative serait d'utiliser la deuxième approche ou autre
progn
.Vous pouvez arrêter de lire ici si ce qui précède vous rend heureux. Mais je pense que c'est une bonne occasion de vous avertir, vous et les autres, de ne pas abuser des macros.
Enfin, bien qu'il soit tentant d'écrire une macro comme
setq-every
"parce que c'est lisp et nous pouvons", je déconseille fortement de le faire. Vous gagnez très peu en le faisant:Mais en même temps, il est beaucoup plus difficile pour les autres lecteurs de lire le code et d'être sûr de ce qui se passe.
setq-every
pourrait être implémenté de diverses manières et d'autres utilisateurs (y compris un futur que vous) ne peuvent pas savoir lequel choisit l'auteur, simplement en regardant le code qui l'utilise. [J'ai initialement fait quelques exemples ici, mais ceux-ci étaient considérés comme sarcastiques et une diatribe par quelqu'un.]Vous pouvez gérer cette surcharge mentale à chaque fois que vous regardez
setq-every
ou vous pouvez simplement vivre avec un seul personnage supplémentaire. (Au moins dans le cas où vous ne devez définir que deux variables, mais je ne me souviens pas que j'ai jamais dû définir plus de deux variables à la même valeur à la fois. Si vous devez le faire, il y a probablement autre chose de mal avec votre code.)D'un autre côté, si vous allez vraiment utiliser cette macro plus d'une demi-douzaine de fois, alors l'indirection supplémentaire en vaut la peine. Par exemple, j'utilise des macros comme celles
--when-let
de ladash.el
bibliothèque. Cela rend plus difficile pour ceux qui le rencontrent dans mon code, mais surmonter cette difficulté de compréhension en vaut la peine car il est utilisé plus que très peu de fois et, du moins à mon avis, il rend le code plus lisible. .On pourrait faire valoir que la même chose s'applique à
setq-every
, mais je pense qu'il est très peu probable que cette macro particulière soit utilisée plusieurs fois. Une bonne règle de base est que lorsque la définition de la macro (y compris la doc-string) ajoute plus de code que l'utilisation de la macro n'en supprime, alors la macro est très probablement exagérée (l'inverse n'est pas nécessairement vrai cependant).la source
setq
vous pouvez référencer sa nouvelle valeur, vous pouvez donc également utiliser cette monstruosité:(setq a 10 b a c a d a e a)
qui définit abcd et e sur 10.Une macro pour faire ce que vous voulez
Comme un exercice en quelque sorte:
Essayez-le maintenant:
Comment ça marche
Puisque les gens sont curieux de savoir comment cela fonctionne (selon les commentaires), voici une explication. Pour vraiment apprendre à écrire des macros, choisissez un bon livre Common Lisp (oui, Common Lisp, vous pourrez faire la même chose dans Emacs Lisp, mais Common Lisp est un peu plus puissant et a de meilleurs livres, à mon humble avis).
Les macros fonctionnent sur du code brut. Les macros n'évaluent pas leurs arguments (contrairement aux fonctions). Nous avons donc ici non évalué
value
et collection devars
, qui pour notre macro ne sont que des symboles.progn
regroupe plusieurssetq
formulaires en un seul. Cette chose:Génère simplement une liste de
setq
formulaires, en utilisant l'exemple d'OP, ce sera:Vous voyez, le formulaire est à l' intérieur du formulaire de guillemets et est préfixé par une virgule
,
. À l'intérieur de la forme entre guillemets, tout est cité comme d'habitude, mais l',
évaluation «active» temporairement, donc toutmapcar
est évalué au moment de la macroexpansion.@
Supprime enfin les parenthèses externes de la liste avecsetq
s, nous obtenons donc:Les macros peuvent transformer arbitrairement votre code source, n'est-ce pas génial?
Une mise en garde
Voici une petite mise en garde, le premier argument sera évalué plusieurs fois, car cette macro se développe essentiellement comme suit:
Vous voyez, si vous avez une variable ou une chaîne ici, c'est OK, mais si vous écrivez quelque chose comme ceci:
Ensuite, votre fonction sera appelée plusieurs fois. Cela peut être indésirable. Voici comment le corriger à l'aide de
once-only
(disponible dans le package MMT ):Et le problème a disparu.
la source
value
au moment de la macro-expansion.(macroexpand '(setq-every user-emacs-directory f-loc1 f-loc2))
->(progn (setq f-loc1 user-emacs-directory) (setq f-loc2 user-emacs-directory))
. Si ellesvalue
étaient évaluées au moment de la macroexpansion, elles seraient remplacées par une chaîne réelle. Vous pouvez mettre un appel de fonction avec des effets secondaires, à la place devalue
, il ne sera pas évalué tant que le code développé ne sera pas exécuté, essayez-le.value
car l'argument de la macro n'est pas évalué automatiquement. Le vrai problème est quevalue
cela sera évalué plusieurs fois, ce qui peut être indésirable,once-only
peut aider ici.mmt-once-only
(qui nécessite un dep externe), vous pouvez utilisermacroexp-let2
.set
, pas d'enveloppersetq
dans une macro. Ou utilisez simplementsetq
avec plusieurs arguments, y compris la répétition des arguments.Puisque vous pouvez utiliser et manipuler des symboles dans lisp, vous pouvez simplement parcourir une liste de symboles et les utiliser
set
.la source