functions defun declarar como and macros lisp

defun - Ejemplos de las macros de Lisp que se pueden usar para



lisp syntax (8)

Además de extender la sintaxis del lenguaje para que pueda expresarse más claramente, también le da control sobre la evaluación. Intenta escribir el tuyo propio en el idioma que elijas para que puedas escribir my_if something my_then print "success" my_else print "failure" y no tener ambas declaraciones print evaluadas. En cualquier lenguaje estricto sin un macro sistema suficientemente poderoso, esto es imposible. Sin embargo, ningún programador de Common Lisp encontraría la tarea demasiado desafiante. Lo mismo ocurre con los bucles, los bucles foreach , etc. No se pueden expresar estas cosas en C porque requieren una semántica de evaluación especial (la gente en realidad intentó introducir foreach en Objective-C, pero no funcionó bien), pero son casi trivial en Common Lisp debido a sus macros.

Escuché que el macro sistema de Lisp es muy poderoso. Sin embargo, me resulta difícil encontrar algunos ejemplos prácticos de para qué pueden usarse; cosas que serían difíciles de lograr sin ellas.

¿Alguien puede dar algunos ejemplos?



Elija cualquier " herramienta de generación de código ". Lee sus ejemplos. Eso es lo que puede hacer.

Excepto que no necesita usar un lenguaje de programación diferente, coloque cualquier código de expansión macro donde se usa la macro, ejecute un comando separado para compilar o tenga archivos de texto adicionales en su disco duro que solo sean valiosos para su compilador .

Por ejemplo, creo que leer el ejemplo de Cog debería ser suficiente para hacer llorar a cualquier programador de Lisp.


R, el lenguaje de programación de estadísticas estándar, tiene macros (manual de R, capítulo 6 - ver www.r-project.org). Puede usar esto para implementar la función lm (), que analiza los datos en función de un modelo que especifique como código.

Así es como funciona: lm(Y ~ aX + b, data) intentará encontrar los parámetros a y b que mejor se ajusten a sus datos. Lo bueno es que puedes sustituir cualquier ecuación lineal por aX + b y seguirá funcionando. Es una característica brillante para facilitar el cálculo estadístico, y solo funciona de manera elegante porque lm () puede analizar la ecuación que se le da, que es exactamente lo que hacen las macros Lisp.


Solo una suposición - Idiomas específicos del dominio.


Transformaciones del código fuente. Todos los tipos.

  • necesitas una declaración WHILE? Tu idioma no tiene uno? ¿Por qué esperar a que el dictador benévolo agregue uno el próximo año? Escríbelo tú mismo. En cinco minutos.

  • Necesitas veinte declaraciones de clase que casi parecen idénticas: solo una cantidad limitada de lugares es diferente. Escriba un formulario macro que tome las diferencias como parámetro y genere el código fuente para usted. ¿Quieres cambiarlo más tarde? Cambia la macro en un solo lugar.

  • ¿Quieres agregar código en el árbol fuente? Una variable realmente debería ser una llamada a función? Envuelva una macro alrededor del código que "recorre" la fuente y cambia los lugares donde encuentra la variable.

  • ¿Quieres escribir tu código en forma de postfix? Use una macro que reescribe el código a la forma normal (prefijo en Lisp).

  • ¿Necesita ejecutar algún código en el entorno del compilador para informar al entorno de desarrollo sobre las definiciones? Las macros pueden generar código que se ejecuta en tiempo de compilación.

  • ¿Desea simplificar algunos códigos en tiempo de compilación? Use una macro que simplifica, de esa manera puede cambiar el trabajo del tiempo de ejecución al tiempo de compilación, en función de los formularios de origen.

  • Necesita escribir una combinación compleja de clases. Por ejemplo, su ventana tiene una clase, los subpanes tienen clases, hay restricciones de espacio entre los paneles, tiene un bucle de comando, un menú y un montón de otras cosas. Escriba una macro que capture la descripción de su ventana y sus componentes y cree las clases y los comandos que dirigen la aplicación, a partir de la descripción.

  • alguna sintaxis de lenguaje no parece muy conveniente? Escriba una macro que lo haga más conveniente para usted, el escritor de la aplicación.

  • ¿Necesita un idioma más cercano al dominio de su aplicación? Cree los formularios de idioma necesarios con un conjunto de macros.

La idea básica: todo lo que está en el nivel lingüístico (nuevas formas, nueva sintaxis, formas de transformación, simplificación, soporte IDE, ...) ahora puede ser programado por el desarrollador pieza por pieza, sin una etapa de procesamiento de macro por separado.


¿Alguna cosa que normalmente quisieras haber hecho en un preprocesador?

Una macro que escribí es para definir máquinas de estado para conducir objetos de juego. Es más fácil leer el código (usando la macro) que leer el código generado:

(def-ai ray-ai (ground (let* ((o (object)) (r (range o))) (loop for p in *players* if (line-of-sight-p o p r) do (progn (setf (target o) p) (transit seek))))) (seek (let* ((o (object)) (target (target o)) (r (range o)) (losp (line-of-sight-p o target r))) (when losp (let ((dir (find-direction o target))) (setf (movement o) (object-speed o dir)))) (unless losp (transit ground)))))

De lo que es para leer:

(progn (defclass ray-ai (ai) nil (:default-initargs :current ''ground)) (defmethod gen-act ((ai ray-ai) (state (eql ''ground))) (macrolet ((transit (state) (list ''setf (list ''current ''ai) (list ''quote state)))) (flet ((object () (object ai))) (let* ((o (object)) (r (range o))) (loop for p in *players* if (line-of-sight-p o p r) do (progn (setf (target o) p) (transit seek))))))) (defmethod gen-act ((ai ray-ai) (state (eql ''seek))) (macrolet ((transit (state) (list ''setf (list ''current ''ai) (list ''quote state)))) (flet ((object () (object ai))) (let* ((o (object)) (target (target o)) (r (range o)) (losp (line-of-sight-p o target r))) (when losp (let ((dir (find-direction o target))) (setf (movement o) (object-speed o dir)))) (unless losp (transit ground)))))))

Al encapsular toda la generación de máquina de estado en una macro, también puedo asegurarme de que solo me refiero a estados definidos y advierto si ese no es el caso.


Las macros son esenciales para proporcionar acceso a las características del lenguaje. Por ejemplo, en TXR Lisp, tengo una sola función llamada sys:capture-cont para capturar una continuación delimitada. Pero esto es incómodo de usar por sí mismo. De modo que hay macros envueltas alrededor de él, como suspend , u obtain y yield que proporcionan modelos alternativos para la ejecución reanudable suspendida. Se implementan aquí .

Otro ejemplo es la defstruct compleja que proporciona sintaxis para definir un tipo de estructura. Compila sus argumentos en lambda -s y otro material que se pasa a la función make-struct-type . Si los programas usaran make-struct-type directamente para definir estructuras OOP, serían feos:

1> (macroexpand ''(defstruct foo bar x y (z 9) (:init (self) (setf self.x 42)))) (sys:make-struct-type ''foo ''bar ''() ''(x y z) () (lambda (#:g0101) (let ((#:g0102 (struct-type #:g0101))) (unless (static-slot-p #:g0102 ''z) (slotset #:g0101 ''z 9))) (let ((self #:g0101)) (setf (qref self x) 42))) ())

¡Ay! Están sucediendo muchas cosas que tienen que ser correctas. Por ejemplo, no solo insertamos un 9 en la ranura z porque (debido a la herencia) podríamos ser realmente la estructura base de una estructura derivada, y en la estructura derivada, z podría ser una ranura estática (compartida por instancias). Estaríamos criticando el valor establecido para z en la clase derivada.

En ANSI Common Lisp, un buen ejemplo de macro es loop , que proporciona un sublengua completo para la iteración paralela. Una invocación de loop único puede expresar un algoritmo complicado completo.

Las macros nos permiten pensar de forma independiente sobre la sintaxis que desearíamos en una función de idioma y las funciones subyacentes o operadores especiales necesarios para implementarla. Cualesquiera que sean las elecciones que hagamos en estos dos, las macros los unirán para nosotros. No tengo que preocuparme de que make-struct sea ​​feo, así que me puedo enfocar en los aspectos técnicos; Sé que la macro puede tener el mismo aspecto, independientemente de cómo realice varias concesiones. Tomé la decisión de diseño de que todas las funciones registradas en el tipo van a realizar todas las inicializaciones de estructuras. De acuerdo, eso significa que mi macro tiene que tomar todas las inicializaciones en la sintaxis de definición de ranura y compilar las funciones anónimas, donde la inicialización de la ranura se realiza mediante el código generado en los cuerpos.

Las macros son compiladores de bits de sintaxis, para los cuales las funciones y los operadores especiales son el idioma de destino.

A veces las personas (normalmente, las personas que no son Lisp) critican las macros de esta manera: las macros no agregan ninguna capacidad, solo azúcar sintáctico.

En primer lugar, el azúcar sintáctico es una capacidad.

En segundo lugar, también debe considerar las macros desde una "perspectiva total de piratas informáticos": la combinación de macros con el trabajo a nivel de implementación. Si agrego funciones a un dialecto de Lisp, como estructuras o continuaciones, en realidad estoy ampliando la potencia. La participación de macros en esa empresa es esencial . Aunque las macros no son la fuente de la nueva potencia (no emana de las macros en sí mismas), ayudan a dominarla y aprovecharla, dándole expresión.

Si no tiene sys:capture-cont , no puede modificar su comportamiento con una macro suspend . Pero si no tiene macros, entonces tiene que hacer algo terriblemente inconveniente para proporcionar acceso a una nueva característica que no es una función de biblioteca, es decir, codificar algunas reglas nuevas de estructura de frase en un analizador sintáctico.