Um die kurze Antwort zu geben, werden Makros zum Definieren von Sprachsyntaxerweiterungen für Common Lisp oder Domain Specific Languages (DSLs) verwendet. Diese Sprachen sind direkt in den vorhandenen Lisp-Code eingebettet. Jetzt können die DSLs eine ähnliche Syntax wie Lisp (wie Peter Norvigs Prolog Interpreter für Common Lisp) oder eine völlig andere (z. B. Infix Notation Math für Clojure) haben.
Hier ist ein konkreteres Beispiel: In
Python sind Listenverständnisse in die Sprache integriert. Dies gibt eine einfache Syntax für einen häufigen Fall. Die Linie
divisibleByTwo = [x for x in range(10) if x % 2 == 0]
liefert eine Liste mit allen geraden Zahlen zwischen 0 und 9. In den 1,5 Tagen von Python gab es keine solche Syntax. Sie würden so etwas eher verwenden:
divisibleByTwo = []
for x in range( 10 ):
if x % 2 == 0:
divisibleByTwo.append( x )
Diese sind beide funktional gleichwertig. Lassen Sie uns unsere Aufhebung des Unglaubens aufrufen und so tun, als hätte Lisp ein sehr begrenztes Schleifenmakro, das nur Iteration ausführt und keine einfache Möglichkeit bietet, das Äquivalent von Listenverständnissen zu erreichen.
In Lisp könnte man folgendes schreiben. Ich sollte beachten, dass dieses erfundene Beispiel so ausgewählt ist, dass es mit dem Python-Code identisch ist, kein gutes Beispiel für Lisp-Code.
;; the following two functions just make equivalent of Python's range function
;; you can safely ignore them unless you are running this code
(defun range-helper (x)
(if (= x 0)
(list x)
(cons x (range-helper (- x 1)))))
(defun range (x)
(reverse (range-helper (- x 1))))
;; equivalent to the python example:
;; define a variable
(defvar divisibleByTwo nil)
;; loop from 0 upto and including 9
(loop for x in (range 10)
;; test for divisibility by two
if (= (mod x 2) 0)
;; append to the list
do (setq divisibleByTwo (append divisibleByTwo (list x))))
Bevor ich weiter gehe, sollte ich besser erklären, was ein Makro ist. Es ist eine Transformation, die Code für Code ausgeführt wird. Das heißt, ein Code, der vom Interpreter (oder Compiler) gelesen wird und Code als Argument verwendet, manipuliert und gibt das Ergebnis zurück, das dann direkt ausgeführt wird.
Das ist natürlich viel Tippen und Programmierer sind faul. So könnten wir DSL für das Listenverständnis definieren. Tatsächlich verwenden wir bereits ein Makro (das Schleifenmakro).
Lisp definiert einige spezielle Syntaxformen. Das Zitat ( '
) gibt an, dass das nächste Token ein Literal ist. Das Quasiquote oder Backtick ( `
) zeigt an, dass das nächste Token ein Literal mit Escapezeichen ist. Escapezeichen werden vom Komma-Operator angezeigt. Das Literal '(1 2 3)
entspricht dem von Python [1, 2, 3]
. Sie können es einer anderen Variablen zuweisen oder an Ort und Stelle verwenden. Sie können sich `(1 2 ,x)
das Äquivalent zu Pythons vorstellen, [1, 2, x]
bei dem x
es sich um eine zuvor definierte Variable handelt. Diese Listennotation ist Teil der Magie, die in Makros steckt. Der zweite Teil ist der Lisp-Reader, der den Code auf intelligente Weise durch Makros ersetzt.
So können wir ein Makro namens lcomp
(kurz für Listenverständnis) definieren. Die Syntax entspricht genau der Python, die wir im Beispiel verwendet haben [x for x in range(10) if x % 2 == 0]
-(lcomp x for x in (range 10) if (= (% x 2) 0))
(defmacro lcomp (expression for var in list conditional conditional-test)
;; create a unique variable name for the result
(let ((result (gensym)))
;; the arguments are really code so we can substitute them
;; store nil in the unique variable name generated above
`(let ((,result nil))
;; var is a variable name
;; list is the list literal we are suppose to iterate over
(loop for ,var in ,list
;; conditional is if or unless
;; conditional-test is (= (mod x 2) 0) in our examples
,conditional ,conditional-test
;; and this is the action from the earlier lisp example
;; result = result + [x] in python
do (setq ,result (append ,result (list ,expression))))
;; return the result
,result)))
Jetzt können wir über die Kommandozeile ausführen:
CL-USER> (lcomp x for x in (range 10) if (= (mod x 2) 0))
(0 2 4 6 8)
Ziemlich ordentlich, oder? Jetzt hört es hier nicht auf. Sie haben einen Mechanismus oder einen Pinsel, wenn Sie möchten. Sie können jede gewünschte Syntax verwenden. Wie die with
Syntax von Python oder C # . Oder die LINQ-Syntax von .NET. Dies ist es, was die Menschen zu Lisp anzieht - ultimative Flexibilität.