es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Usando macroexpand-1 para expandir macros dentro de formas let (Practical Common Lisp, Capítulo 8, “Tapando las Fugas”)

En el Capítulo 8 de Practical Common Lisp, “Tapando las filtraciones”, definimos esta macro y descubrimos que sufre una fuga tras examinarla con macroexpand-1.

lang-lisp
(defmacro do-primes ((var start end) &body body)
(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
(ending-value ,end))
((> ,var ending-value))
,@body))
`

La siguiente llamada aparentemente inocente a do-primes no funciona correctamente debido a esta fuga:

lang-lisp
(do-primes (ending-value 0 10) (print ending-value))

Tampoco esta otra:

lang-lisp
(let ((ending-value 0)) (do-primes (p 0 10)
(incf ending-value p)) ending-value)

Puedo usar macroexpand-1 de la siguiente manera, en la que he nombrado do-primes4 a esta macro:

lang-lisp
(macroexpand-1 '(do-primes4 (ending-value 0 10)
(print ending-value)))

lang-lisp
(DO ((ENDING-VALUE (NEXT-PRIME 0) (NEXT-PRIME (1+ ENDING-VALUE)))
(ENDING-VALUE 10))
((> ENDING-VALUE ENDING-VALUE))
(PRINT ENDING-VALUE))

Sin embargo, no puedo hacer lo mismo en la segunda forma en la que se llama a la macro con el let:

lang-lisp
(macroexpand-1 '(let ((ending-value 0))
(do-primes4 (p 0 10)
(incf ending-value p))
ending-value))

lang-lisp
(LET ((ENDING-VALUE 0))
(DO-PRIMES4 (P 0 10)
(INCF ENDING-VALUE P))
ENDING-VALUE)

Parece que en realidad no se reduce a la macro do-primes4. Estoy usando SBCL.

Tags:  , ,

Answer

  1. Avatar for davy.ai

    En el Capítulo 8 de Practical Common Lisp, se define una macro llamada do-primes que tiene una fuga que puede ser detectada examinándola con macroexpand-1. La definición de la macro es la siguiente:

    (defmacro do-primes ((var inicio fin) &body cuerpo)
      `(do ((,var (next-prime ,inicio) (next-prime (1+ ,var)))
            (ending-value ,fin))
           ((> ,var ending-value))
         ,@cuerpo))
    

    La fuga se produce porque la macro utiliza el mismo símbolo ending-value tanto para la variable de bucle como para el límite superior del bucle. Esto puede causar problemas cuando la variable de bucle se modifica dentro del bucle ya que afectará también al límite superior.

    Para demostrar el problema, el libro proporciona dos ejemplos en los que la macro no funciona correctamente. El primer ejemplo es:

    (do-primes (ending-value 0 10)
      (print ending-value))
    

    El segundo ejemplo es:

    (let ((ending-value 0))
      (do-primes (p 0 10)
        (incf ending-value p))
      ending-value)
    

    Cuando usamos macroexpand-1 en el primer ejemplo, obtenemos el siguiente código expandido:

    (do ((ending-value (next-prime 0) (next-prime (1+ ending-value)))
         (ending-value 10))
        ((> ending-value ending-value))
      (print ending-value))
    

    Este código muestra que el símbolo ending-value se utiliza tanto para la variable de bucle como para el límite superior, lo que puede causar problemas como se explicó anteriormente.

    Sin embargo, cuando usamos macroexpand-1 en el segundo ejemplo, no obtenemos el código expandido esperado. En su lugar, obtenemos el mismo código que antes:

    (let ((ending-value 0))
      (do-primes (p 0 10)
        (incf ending-value p))
      ending-value)
    

    Esto sugiere que la expansión de la macro no se está realizando como se esperaba. No está claro por qué está sucediendo esto, pero podría ser una peculiaridad de la implementación o algo específico de SBCL.

Comments are closed.