para dar la respuesta definitiva, las macros se utilizan para definir las extensiones de la sintaxis del lenguaje de Common Lisp o de dominio de idiomas específicos (DSL). Estos idiomas están incrustados en el código Lisp existente. Ahora, las DSL pueden tener una sintaxis similar a Lisp (como el Prolog Interpreter de Peter Norvig para Common Lisp) o completamente diferente (por ejemplo, Infix Notation Math para Clojure).
Aquí hay un ejemplo más concreto:
Python tiene listas de comprensiones integradas en el lenguaje. Esto proporciona una sintaxis simple para un caso común. La línea
divisibleByTwo = [x for x in range(10) if x % 2 == 0]
produce una lista que contiene todos los números pares entre 0 y 9. En los Python 1.5 días no había tal sintaxis; usted usaría algo más así:
divisibleByTwo = []
for x in range(10):
if x % 2 == 0:
divisibleByTwo.append(x)
Ambos son funcionalmente equivalentes. Invocamos nuestra suspensión de la incredulidad y pretendemos que Lisp tiene una macro de bucle muy limitada que solo hace iteración y no es una manera fácil de hacer el equivalente a la lista de comprensión.
En Lisp puede escribir lo siguiente. Debo señalar que este ejemplo artificial se escogió para ser idéntico al código Python, no un buen ejemplo de código 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))))
Antes de ir más allá, debería explicar mejor qué es una macro. Es una transformación realizada en el código por el código. Es decir, un fragmento de código, leído por el intérprete (o compilador), que toma el código como argumento, lo manipula y devuelve el resultado, que luego se ejecuta en el lugar.
Por supuesto que es mucha mecanografía y los programadores son flojos. Entonces podríamos definir DSL para hacer listas de comprensión. De hecho, ya estamos usando una macro (la macro de bucle).
Lisp define un par de formas de sintaxis especiales. La cita ('
) indica que el siguiente token es un literal. El cuasiquote o backtick (`
) indica que el siguiente token es un literal con escapes. Los escapes son indicados por el operador de coma. El literal '(1 2 3)
es el equivalente al [1, 2, 3]
de Python. Puede asignarlo a otra variable o usarlo en su lugar. Puedes pensar en `(1 2 ,x)
como el equivalente de Python [1, 2, x]
donde x
es una variable previamente definida. Esta notación de lista es parte de la magia que entra en las macros. La segunda parte es el lector Lisp que inteligentemente sustituye las macros por el código, pero eso se ilustra mejor a continuación:
Así que podemos definir una macro llamada lcomp
(abreviatura de comprensión de la lista). Es la sintaxis será exactamente igual que la pitón que hemos utilizado en el ejemplo [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)))
Ahora podemos ejecutar en la línea de comandos:
CL-USER> (lcomp x for x in (range 10) if (= (mod x 2) 0))
(0 2 4 6 8)
Bastante limpio, ¿eh? Ahora no se detiene allí. Tienes un mecanismo, o un pincel, si quieres. Puede tener cualquier sintaxis que pueda desear. Como la sintaxis de Python o C# with
. O la sintaxis LINQ de .NET. En resumen, esto es lo que atrae a las personas a Lisp: máxima flexibilidad.
Por cierto, hay un procesador de macros de estilo LISP para C# llamado Lemp: http://ecsharp.net/lemp/ ... JavaScript también ha llamado Sweet.js: https://www.sweetjs.org/ – Qwertie
@Qwertie ¿Los sweetjs incluso funcionan en estos días? –
No lo he usado, pero el compromiso más reciente fue hace seis meses ... ¡lo suficientemente bueno para mí! – Qwertie