En voici une que j'utilise pour le débogage (dans Clojure):
user=> (defmacro print-var [varname] `(println ~(name varname) "=" ~varname))
#'user/print-var
=> (def x (reduce * [1 2 3 4 5]))
#'user/x
=> (print-var x)
x = 120
nil
J'ai dû faire face à une table de hachage roulée à la main en C ++, où la get
méthode a pris une référence de chaîne non const comme argument, ce qui signifie que je ne peux pas l'appeler avec un littéral. Pour rendre cela plus facile à gérer, j'ai écrit quelque chose comme ceci:
#define LET(name, value, body) \
do { \
string name(value); \
body; \
assert(name == value); \
} while (false)
Bien que quelque chose comme ce problème soit peu susceptible de se produire dans lisp, je trouve particulièrement agréable que vous puissiez avoir des macros qui n'évaluent pas leurs arguments deux fois, par exemple en introduisant une vraie liaison de let. (Admis, ici j'aurais pu m'en sortir).
J'ai également recours au hack horriblement laid de l'emballage de choses dans un do ... while (false)
tel que vous pouvez l'utiliser dans la partie alors d'un si et que la partie autre fonctionne toujours comme prévu. Vous n'avez pas besoin de cela dans lisp, qui est une fonction de macros opérant sur des arbres de syntaxe plutôt que des chaînes (ou des séquences de jetons, je pense, dans le cas de C et C ++) qui subissent ensuite une analyse.
Il existe quelques macros de threading intégrées qui peuvent être utilisées pour réorganiser votre code de manière à ce qu'il soit plus clair («threading» comme dans «semer votre code ensemble», pas le parallélisme). Par exemple:
(->> (range 6) (filter even?) (map inc) (reduce *))
Il prend la première forme, (range 6)
et en fait le dernier argument de la forme suivante (filter even?)
, qui à son tour devient le dernier argument de la forme suivante et ainsi de suite, de sorte que ce qui précède est réécrit en
(reduce * (map inc (filter even? (range 6))))
Je pense que le premier se lit beaucoup plus clairement: "prenez ces données, faites-y ceci, puis faites cela, puis faites l'autre et nous avons terminé", mais c'est subjectif; ce qui est objectivement vrai, c'est que vous lisez les opérations dans l'ordre où elles sont effectuées (en ignorant la paresse).
Il existe également une variante qui insère le formulaire précédent en tant que premier (plutôt que dernier) argument. Un cas d'utilisation est l'arithmétique:
(-> 17 (- 2) (/ 3))
Lit "prendre 17, soustraire 2 et diviser par 3".
En parlant d'arithmétique, vous pouvez écrire une macro qui analyse la notation infixée, de sorte que vous pourriez dire par exemple (infix (17 - 2) / 3)
et qu'elle cracherait ce (/ (- 17 2) 3)
qui a l'inconvénient d'être moins lisible et l'avantage d'être une expression lisp valide. C'est la partie sous-langage DSL / données.