2011-12-24 1 views
6

Sto cercando di implementare una macro per convertire in modo ricorsivo una lista di infissi in una prefisso. Incontro un problema come segue:In clojure, come eseguire la modellazione del codice durante l'implementazione di una macro mediante ricorsione

;;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] 

Dove è il problema? Come definire correttamente una macro con il codice dei template?

P.S. Ho cambiato il codice in questo e funziona, perché? qual è la differenza ?:

(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

ha qualcosa a che fare con la messa "blocco if" nella gamma vincolante di backquote? – lkahtz

risposta

13

Nella versione originale, il controllo (if (not ...)) stava accadendo al momento della compilazione; lo hai invece incluso nel codice espanso. Quindi ecco un cambiamento minimo che porterebbe a comportarsi come si desidera - è in effetti lo stesso dell'originale, ma "capovolgere" ciò che viene citato e ciò che non lo è.

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

Tuttavia, è un po 'più bello da usare destrutturazione per tirare fuori i pezzi di forma prima del tempo, piuttosto che sul posto:

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

E meglio ancora, in realtà, è quello di spostare il caso non ricorsivo di fuori, in modo che (a) funziona per i numeri letterali, e (b) il codice sembra più simile a quello che si espande a:

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

Grazie mille @amalloy - questo mi dà un modo molto complicato per attaccare il mio problema (https://stackoverflow.com/questions/41555991/mismatched-argument-count-to-recur-in-syntax-quoted-macro) , ma sto cercando di capire i passaggi esatti nell'ultimo blocco di codice. La ricorsione funziona correttamente in questo caso? Sto passando (1 + 2 + 3) come 'form', ma restituisce solo la somma dei primi due operandi. Inoltre, ho ragione a pensare che il 'seq?' È controllare che il valore 'y' sia ricorrente e che sia una forma valida per determinare se ricorrere o meno? – Ooberdan