Folgendes verwende ich zum Debuggen (in 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
Ich musste mich in C ++ mit einer von Hand gerollten Hash-Tabelle auseinandersetzen, bei der die get
Methode eine nicht konstante Zeichenfolgenreferenz als Argument verwendete, was bedeutet, dass ich sie nicht mit einem Literal aufrufen kann. Um das einfacher zu machen, habe ich Folgendes geschrieben:
#define LET(name, value, body) \
do { \
string name(value); \
body; \
assert(name == value); \
} while (false)
Es ist zwar unwahrscheinlich, dass dieses Problem auftaucht, aber ich finde es besonders schön, dass Sie Makros haben können, die ihre Argumente nicht zweimal auswerten, indem Sie beispielsweise einen Real einführen Let-Bindung . (Zugegeben, hier hätte ich mich zurechtfinden können).
Ich greife auch auf den furchtbar hässlichen Trick zurück, Sachen in ein Päckchen zu wickeln do ... while (false)
so etwas dass man sie im damaligen Teil eines If verwenden kann und das übrige Teil immer noch wie erwartet funktioniert. Sie brauchen dies nicht in lisp, das ist eine Funktion von Makros, die auf Syntaxbäumen ausgeführt werden, anstatt von Strings (oder Token-Sequenzen, glaube ich, im Fall von C und C ++), die dann analysiert werden.
Es gibt einige eingebaute Threading-Makros, mit denen Sie Ihren Code so reorganisieren können, dass er sauberer gelesen wird ("Threading" wie "Code zusammenfügen", nicht Parallelität). Beispielsweise:
(->> (range 6) (filter even?) (map inc) (reduce *))
Es nimmt die erste Form an (range 6)
und macht es zum letzten Argument der nächsten Form, (filter even?)
die wiederum zum letzten Argument der nächsten Form usw. gemacht wird, so dass das Obige umgeschrieben wird
(reduce * (map inc (filter even? (range 6))))
Ich denke, das erste liest sich viel klarer: "Nehmen Sie diese Daten, machen Sie das, dann machen Sie das, dann machen Sie das andere und wir sind fertig", aber das ist subjektiv; Objektiv gesehen stimmt es, dass Sie die Operationen in der Reihenfolge lesen, in der sie ausgeführt werden (Faulheit wird ignoriert).
Es gibt auch eine Variante, bei der die vorherige Form als erstes (und nicht als letztes) Argument eingefügt wird. Ein Anwendungsfall ist die Arithmetik:
(-> 17 (- 2) (/ 3))
Liest als "nehmen Sie 17, subtrahieren Sie 2 und teilen Sie durch 3".
Apropos Arithmetik: Sie können ein Makro schreiben, das das Parsen von Infixnotationen ausführt, so dass Sie beispielsweise sagen können, dass (infix (17 - 2) / 3)
es ausspuckt, (/ (- 17 2) 3)
was den Nachteil hat, dass es weniger lesbar ist und den Vorteil hat, ein gültiger Lisp-Ausdruck zu sein. Das ist der Teil der DSL / Daten-Subsprache.