Per fornire una risposta breve, le macro vengono utilizzate per definire le estensioni della sintassi della lingua in Common Lisp o Domain Specific Languages (DSL). Queste lingue sono integrate direttamente nel codice Lisp esistente. Ora, i DSL possono avere una sintassi simile a Lisp (come Prolog Interpreter for Common Lisp di Peter Norvig ) o completamente diversa (ad esempio Infix Notation Math for Clojure).
Ecco un esempio più concreto:
Python ha una comprensione dell'elenco integrata nel linguaggio. Ciò fornisce una sintassi semplice per un caso comune. La linea
divisibleByTwo = [x for x in range(10) if x % 2 == 0]
restituisce un elenco contenente tutti i numeri pari tra 0 e 9. In Python 1,5 giorni non vi era tale sintassi; useresti qualcosa di più simile a questo:
divisibleByTwo = []
for x in range( 10 ):
if x % 2 == 0:
divisibleByTwo.append( x )
Questi sono entrambi funzionalmente equivalenti. Invociamo la nostra sospensione dell'incredulità e facciamo finta che Lisp abbia una macro di loop molto limitata che fa solo iterazione e nessun modo semplice per fare l'equivalente delle comprensioni dell'elenco.
In Lisp potresti scrivere quanto segue. Devo notare che questo esempio inventato è scelto per essere identico al codice Python, non un buon esempio di codice Lisp.
;; 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))))
Prima di andare oltre, dovrei spiegare meglio cos'è una macro. È una trasformazione eseguita su codice per codice. Cioè, un pezzo di codice, letto dall'interprete (o dal compilatore), che accetta il codice come argomento, manipola e restituisce il risultato, che viene quindi eseguito sul posto.
Ovviamente è un sacco di battitura e i programmatori sono pigri. Quindi potremmo definire DSL per fare la comprensione della lista. In effetti, stiamo già utilizzando una macro (la macro del ciclo).
Lisp definisce un paio di forme speciali di sintassi. La virgoletta ( '
) indica che il token successivo è un valore letterale. Il quasiquote o backtick ( `
) indica che il token successivo è un letterale con escape. Le escape sono indicate dall'operatore virgola. Il letterale '(1 2 3)
è l'equivalente di Python [1, 2, 3]
. Puoi assegnarlo a un'altra variabile o usarlo sul posto. Puoi pensare `(1 2 ,x)
come l'equivalente di Python [1, 2, x]
dove x
è una variabile precedentemente definita. Questa notazione dell'elenco fa parte della magia che va nelle macro. La seconda parte è il lettore Lisp che sostituisce in modo intelligente le macro per il codice ma che è meglio illustrato di seguito:
Quindi possiamo definire una macro chiamata lcomp
(abbreviazione di comprensione dell'elenco). La sua sintassi sarà esattamente come il pitone che abbiamo usato nell'esempio [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)))
Ora possiamo eseguire dalla riga di comando:
CL-USER> (lcomp x for x in (range 10) if (= (mod x 2) 0))
(0 2 4 6 8)
Abbastanza pulito, eh? Ora non si ferma qui. Hai un meccanismo, o un pennello, se vuoi. Puoi avere qualsiasi sintassi che potresti desiderare. Come la with
sintassi di Python o C # . O la sintassi LINQ di .NET. Alla fine, questo è ciò che attrae le persone a Lisp: la massima flessibilità.