Вкратце, как сделать шаблонизацию кода при реализации макроса с использованием рекурсии - PullRequest
6 голосов
/ 24 декабря 2011

Я пытаюсь реализовать макрос для рекурсивного преобразования списка инфиксов в префикс Я столкнулся с проблемой следующим образом:

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

Где проблема? Как правильно определить макрос с шаблоном кода?

P.S. Я изменил код на это, и это работает, почему? в чем разница?:

(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)))))

1 Ответ

13 голосов
/ 24 декабря 2011

В оригинальной версии проверка (if (not ...)) происходила во время компиляции; вместо этого вы включили его в расширенный код. Итак, вот минимальное изменение, которое заставит его работать так, как вы хотите - оно фактически совпадает с оригиналом, но «переворачивает» то, что указано, а что нет.

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

Тем не менее, немного лучше использовать деструктуризацию, чтобы вытащить части формы раньше, чем на месте:

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

И, что еще лучше, действительно, переместить нерекурсивный регистр наружу, чтобы (а) он работал для литеральных чисел, и (б) код больше походил на то, что он расширяет:

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