2011-11-30 11 views
5

Estoy intentando escribir una macro en Lisp que devuelve la expresión enésima pasado a él y sólo evalúa la expresión. Por ejemplo:Lisp expresiones que evalúan macro cuando no lo quieren a

(let ((n 2)) 
    (nth-expr n (/ 1 0) (+ 1 2) (/ 1 0))) 

debe devolver 3. Estoy obteniendo un error de división por 0. Mi definición de macro es la siguiente:

(defmacro nth-expr (n &rest expressions) 
    (let ((i (gensym)) 
     (expr (gensym))) 
    `(do ((,i 1 (1+ ,i)) 
      (,expr ,expressions (cdr ,expr))) 
     ((= ,i ,n) (car ,expr))))) 

¿Alguna idea de lo que estoy haciendo mal? Gracias.

EDIT:

Gracias a @Vsevolod Dyomkin por ayudarme con la parte de arriba. Ahora hay un problema más. Cuando trato de hacer

(let ((n 3) (x "win") (y "lose")) 
    (nth-expr n (princ x) (princ x) (princ y) (princ x))) 

estoy consiguiendo el error Error: Attempt to take the value of the unbound variable 'Y'.

Mi código actualizado es el siguiente:

(defmacro nth-expr (n &rest expressions) 
    (let ((i (gensym)) 
     (expr (gensym)) 
     (num (gensym))) 
    `(let ((,num (eval ,n))) 
     (do ((,i 1 (1+ ,i)) 
      (,expr ',expressions (cdr ,expr))) 
      ((= ,i ,num) (eval (car ,expr))))))) 
+1

¿Por qué llama 'eval' en una macro? – leppie

+0

@leppie porque si no lo hago, regresa (princ y) pero quiero que eso se evalúe. – Daniel

+1

'Y' no existe en esa etapa. ¿Que estás tratando de hacer? – leppie

Respuesta

1

Tienes que qoute,expressions, así:

(defmacro nth-expr (n &rest expressions) 
    (let ((i (gensym)) 
     (expr (gensym))) 
    `(do ((,i 1 (1+ ,i)) 
      (,expr ',expressions (cdr ,expr))) 
     ((= ,i ,n) (car ,expr))))) 

De lo contrario, lo que se obtiene es esto - la lista es expressions insertado como es:

CL-USER> (let ((n 2)) 
      (macroexpand-1 '(nth-expr n (/ 1 0) (+ 1 2) (/ 1 0)))) 
(DO ((#:G864 1 (1+ #:G864)) 
    (#:G865 ((/ 1 0) (+ 1 2) (/ 1 0)) (CDR #:G865))) 
    ((= #:G864 N) (CAR #:G865))) 
+0

Ah, eso es útil. Entonces me imagino que tendré que lanzar un 'eval' para evaluar' (+ 1 2) 'en lugar de solo devolver' '(+ 1 2) ' – Daniel

+0

He hecho una pregunta de seguimiento más arriba. ¿Crees que podrías intentar ayudar con eso? Gracias. – Daniel

+0

Yo diría que la forma más fácil de lograr su objetivo, es decir, evaluar solo la n-ésima expresión, sería algo así como: '(defmacro nth-expr (n & body expressions) (nth n expressions))' –

5

Lo principal es FIRST come arriba con la expansión

Cuál debería ser el código de trabajo parece? ¿El código al que se expandiría el uso de macros?

A continuación se escribe la macro para crear dicho código.

Asegúrese de no evaluar ninguno de los códigos provistos en la macro.

Un objetivo expansión útil simple para su problema es una forma CASE.

(case n 
    (0 (do-this)) 
    (1 (do-that)) 
    (2 (do-something-else))) 

Ahora debe ser fácil escribir una macro que se expande en una forma (nth-expr n (/ 1 0) (+ 1 2) (/ 1 0)) CASO ...

4

No es necesario eval aquí, y no hay necesidad de almacenar expresiones en una lista.

Una aplicación adecuada es el siguiente:

(defmacro nth-expr (n &rest expressions) 
    `(case ,n 
     ,@(loop for e in expressions 
       for n from 1 
       collect 
       `((,n) ,e)))) 

Su ejemplo se expande a medida:

(CASE N ((1) (/ 1 0)) ((2) (+ 1 2)) ((3) (/ 1 0))) 
0

El error en el segundo ejemplo es que EVAL no puede ver su X y Y variables léxico vinculado N, .

De los CLHS para EVAL:

Evalúa forma en el actual entorno dinámico y el entorno léxico nula.

Sería funciona si usted declaró X y Y tan especial:

(let ((n 3) (x "win") (y "lose")) 
    (declare (special x y)) 
    (nth-expr n (princ x) (princ x) (princ y) (princ x))) 

pero esto todavía no es tan buena como la solución sugerida por CASESK-logic.

Cuestiones relacionadas