Ein lustiger Aspekt von Makros ist, dass sie Ihnen die Möglichkeit geben, die Syntax Ihres Lisp zu erweitern und neue syntaktische Funktionen hinzuzufügen. Dies geschieht nur, weil an ein Makro übergebene Argumente nur zur Laufzeit und nicht zum Zeitpunkt von ausgewertet werden Kompilieren Sie Ihr Makro. Als Beispiel -> ->>
bewerten die ersten / letzten Thread-Makros in clojure ihre Ausdrucksargumente nicht, andernfalls könnten diese ausgewerteten Ergebnisse nichts mehr akzeptieren (sagen wir (+ 3) => 3
und 3
ist keine Funktion, die Ihr erstes Hauptargument in so etwas akzeptieren könnte (-> 4 (+ 3))
).
Wenn mir diese Syntax gefällt und ich sie meiner Common Lisp-Implementierung hinzufügen möchte (die sie nicht hat), kann ich sie addieren, indem ich selbst ein Makro definiere. Etwas wie das:
;;; in SBCL
;;; first thread:
(defmacro -> (x &rest more)
(loop for m in more
for n = `(,(first m) ,x ,@(rest m)) then `(,(first m) ,n ,@(rest m))
finally (return n)))
;;; last thread:
(defmacro ->> (x &rest more)
(loop for m in more
for n = `(,(first m) ,@(rest m) ,x) then `(,(first m) ,@(rest m) ,n)
finally (return n)))
Jetzt könnte ich sie in Common Lisp genauso verwenden wie in Clojure:
(-> #'+
(mapcar '(2 3 4) '(1 2 3))) ;; => (3 5 7)
(->> #'>
(sort '(9 8 3 5 7 2 4))) ;; => (9 8 7 5 4 3 2)
Vielleicht möchten Sie auch eine neue Syntax für die range
Funktion von clojure mit Ihren eigenen Schlüsselwörtern für Ihre Syntax haben, etwa:
(from 0 to 10) ;=> (0 1 2 3 4 5 6 7 8 9)
(from 0 to 10 by 0.5) ;;=> (0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5)
kann wie folgt definiert werden:
(defmacro from
"Just another range!"
[x y z & more]
`(when (= '~y '~'to)
(if '~more (range ~x ~z (second '~more))
(range ~x ~z))))
oder fügen Sie etwas hinzu, das Common Lisp ähnelt (nur zum Beispiel, da all dies natürlich bereits in den Sprachen möglich ist!):
(defmacro from (x y z &rest r)
`(when (eql ',y 'to)
(if (and ',r (eql (first ',r) 'by))
(loop for i from ,x to ,z by (second ',r) collect i)
(loop for i from ,x to ,z collect i))))
(from 0 to 10) ;=> (0 1 2 3 4 5 6 7 8 9 10)
(from 0 to 5 by 1/2) ;=> (0 1/2 1 3/2 2 5/2 3 7/2 4 9/2 5)