2010-04-01 10 views

Respuesta

2

Solo supongo - Idiomas específicos del dominio.

+0

Esta pregunta tiene un sentido de CW, así que publiqué como tal. –

+1

Pero, ¿qué tienen las macros que las hacen superiores a escribir lenguajes específicos de dominio sobre funciones normales? –

+1

JtS: Necesitaría una gran cantidad de citas (sin duda una función relacionada) para retrasar la evaluación de todo, por ejemplo. Las macros te permiten evitar eso al permitirte hacer cosas arbitrarias en tiempo de compilación. – Ken

8

elegir cualquier "code generation tool". Lee sus ejemplos. Eso es lo que puede hacer.

Excepto que no necesita usar un lenguaje de programación diferente, poner cualquier código de macro expansión donde se usa la macro, ejecutar un comando separado para compilar o tener archivos de texto adicionales en su disco duro que son solo de valor para su compilador.

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

37

Transformaciones de código fuente. Todos los tipos. Ejemplos:

  • nuevas sentencias de control: Es necesario una sentencia 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.

  • Código más corto: Necesita veinte declaraciones de clase que casi se ven 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.

  • Reemplazos en el árbol de código fuente: ¿Desea agregar el código en el árbol de código 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.

  • Sintaxis de Postfix: ¿Desea escribir su código en forma de postfix? Use una macro que reescribe el código a la forma normal (prefijo en Lisp).

  • Tiempo de compilación de efectos: ¿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.

  • Simplificaciones/optimizaciones de código 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.

  • Generación de código a partir de descripciones/configuraciones: 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.

  • Mejoras en la sintaxis: ¿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.

  • Idiomas específicos del dominio: Necesita un idioma que esté más cerca del dominio de su aplicación? Cree los formularios de idioma necesarios con un conjunto de macros.

metalingüística abstracción

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

+0

Lista maravillosa de razones para usar macros. Siempre es un placer leer tus respuestas. Gracias. – gsl

2

Además de extender la sintaxis del lenguaje para que pueda expresarse más claramente, también le da control sobre la evaluación. Intente escribir su propio if en el idioma de su elección para que realmente pueda escribir my_if something my_then print "success" my_else print "failure" y no tener ambas declaraciones de impresión 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. Ídem para for bucles, foreach bucles, etc. No se pueden expresar estas cosas en C porque requieren una semántica de evaluación especial (la gente realmente intentó introducir foreach en Objective-C, pero no funcionó bien), pero son casi trivial en Common Lisp debido a sus macros.

3

R, el lenguaje de programación de estadísticas estándar, tiene macros (R manual, chapter 6). 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.

4

¿Algo que normalmente querrías haber hecho en un preprocesador?

Una macro que escribí, es para definir máquinas estatales para manejar objetos del juego.Es más fácil de leer el código (usando la macro) de lo que es para 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 estados en una macro, también puedo asegurarme de que solo me refiero a estados definidos y advierto si ese no es el caso.

+8

¿Podría ser un poco más específico en cuanto a por qué es "código no bueno"? Estoy más que feliz de tener críticas sobre mi código, pero se agradecerá algo un poco más constructivo que "eso no es bueno". Estoy bastante seguro de que tienes mi dirección de correo electrónico si el cuadro de comentarios es demasiado pequeño. – Vatine

1

Las macros son esenciales para proporcionar acceso a las funciones de idioma. 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 alrededor, como suspend o obtain and yield, que proporcionan modelos alternativos para la ejecución reanudable suspendida. Se implementan here.

Otro ejemplo es la macro compleja defstruct 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 utilizados make-struct-type directamente para definir estructuras de programación orientada a objetos, que sería feo:

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))) 
        ()) 

Huy! Están sucediendo muchas cosas que tienen que ser correctas. Por ejemplo, no solo insertamos 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 sola invocación a loop puede expresar un algoritmo complicado completo.

Las macros nos permiten pensar independientemente sobre la sintaxis que nos gustaría en una función de idioma, y ​​las funciones subyacentes o los 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 de usar, por lo que puedo centrarme 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, cuyas funciones y operadores especiales son el idioma de destino.

A veces las personas (las personas que no son Lisp, por lo general) 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": combinando 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), ayudan a dominarla y aprovecharla, dándole expresión.

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

Cuestiones relacionadas