2011-12-24 29 views
6

Estoy tratando de implementar una macro para convertir recursivamente una lista infija en una prefijo. Me encuentro con un problema de la siguiente manera:En clojure, cómo hacer plantillas de código al implementar una macro usando la recursión

;;this works 
(defmacro recursive-infix [form] 
    (list (second form) (first form) 
     (if (not (seq? (nth form 2))) 
      (nth form 2) 
      (recursive-infix (nth form 2))))) 

;;this doesn't work 
(defmacro my-recursive-infix [form] 
    `(~(second form) ~(first form) 
     (if (not (seq? ~(nth form 2))) 
      ~(nth form 2) 
      (my-recursive-infix ~(nth form 2))))) 

(macroexpand '(recursive-infix (10 + 10))) 
;;get (+ 10 10) 

(macroexpand '(my-recursive-infix (10 + 10))) 
;;get (+ 10 (if (clojure.core/not (clojure.core/seq? 10)) 10 (user/my-recursive-infix 10))) 

(recursive-infix (10 + 10)) 
;;get 20 

(my-recursive-infix (10 + 10)) 
;;Don't know how to create ISeq from: java.lang.Integer [Thrown class java.lang.IllegalArgumentException] 

¿Dónde está el problema? ¿Cómo definir correctamente una macro con plantillas de código?

P.S. Cambié el código a esto y funciona, ¿por qué? ¿cuál es la diferencia ?:

(defmacro my-recursive-infix [form] 
    (if (not (seq? (nth form 2))) 
    `(~(second form) ~(first form) ~(nth form 2)) 
    `(~(second form) ~(first form) (my-recursive-infix (nth form 2))))) 
+0

¿tiene algo que ver con poner "si el bloque" en el rango vinculante de la contracuenta? – lkahtz

Respuesta

13

En la versión original, el cheque (if (not ...)) estaba sucediendo en tiempo de compilación; en cambio, lo ha incluido en el código expandido. Así que aquí hay un cambio mínimo que haría que actuara como quisieras, efectivamente es lo mismo que el original, pero "volteando" lo que se cita y lo que no.

(defmacro my-recursive-infix [form] 
    (let [third (nth form 2)] 
    `(~(second form) ~(first form) 
     ~(if (not (seq? third)) 
     third 
     `(my-recursive-infix ~third))))) 

Sin embargo, es un poco más agradable de usar desestructuración de sacar las piezas de la forma antes de tiempo, en lugar de en el lugar:

(defmacro my-recursive-infix [form] 
    (let [[x op y] form] 
    `(~op ~x ~(if (not (seq? y)) 
       y 
       `(my-recursive-infix ~y))))) 

Y mejor aún, en realidad, es mover el caso no recursivo exterior, de manera que (a) funciona para los números literales, y (b) el código se parece más a lo que se expande a:

(defmacro my-recursive-infix [form] 
    (if-not (seq? form) 
    form 
    (let [[x op y] form] 
     `(~op ~x (my-recursive-infix ~y))))) 
+0

Muchas gracias @amalloy - esto realmente me da una forma menos complicada de atacar mi problema (https://stackoverflow.com/questions/41555991/mismatched-argument-count-to-recur-in-syntax-quoted-macro) , pero estoy tratando de entender los pasos exactos en el último bloque de código. ¿La recursividad funciona correctamente en este caso? Paso (1 + 2 + 3) como 'forma', pero solo devuelve la suma de los dos primeros operandos. Además, ¿estoy en lo cierto al pensar que 'seq?' Es verificar que el valor 'y' recurrente sea un formulario válido para determinar si se repite o no nuevamente. – Ooberdan

Cuestiones relacionadas