2011-12-24 11 views
6

Próbuję zaimplementować makro rekurencyjnie przekształcające listę infix na prefiks jeden. Wystąpił problem w następujący sposób:W clojure, jak wykonać szablon templating podczas implementacji makra za pomocą rekursji

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

Gdzie jest problem? Jak poprawnie zdefiniować makro z szablonem kodu?

P.S. Zmieniłem kod w tym i działa, dlaczego? jaka jest różnica ?:

(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

czy to ma coś wspólnego z wprowadzeniem „jeśli blok” w wiążącym zakresie wstecznego cytatu? – lkahtz

Odpowiedz

13

W oryginalnej wersji, check (if (not ...)) się dzieje w czasie kompilacji; zamiast tego umieściłeś go w rozszerzonym kodzie. Oto minimalna zmiana, która sprawi, że będzie zachowywać się tak, jak chcesz - jest faktycznie taki sam jak oryginał, ale "obraca" to, co jest cytowane, a co nie.

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

Jednak to trochę ładniejszy używać destructuring wyciągnąć kawałki postaci z wyprzedzeniem, a nie w miejscu:

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

i lepiej jeszcze, naprawdę, jest przeniesienie non-rekurencyjne przypadek na zewnątrz, tak że (a) działa dla liczb dosłownych, oraz (b) kod wygląda bardziej jak to, co rozszerza do:

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

Bardzo dziękuję @amalloy - to naprawdę daje mi mniej skomplikowany sposób na zaatakowanie mojego problemu (https://stackoverflow.com/questions/41555991/mismatched-argument-count-to-recur-in-syntax-quoted-macro) , ale staram się zrozumieć dokładne kroki w ostatnim bloku kodu. Czy rekursja działa poprawnie w tym przypadku? Przechodzę (1 + 2 + 3) jako "forma", ale zwracam tylko sumę pierwszych dwóch operandów. Ponadto, czy mam rację, myśląc, że "seq?" Polega na sprawdzeniu, czy powtarzana wartość "y" jest poprawną formą, aby ustalić, czy ponownie powrócić? – Ooberdan

Powiązane problemy