在clojure中,如何在使用递归实现宏时进行代码模板化

lka*_*htz 6 macros clojure

我正在尝试实现一个宏来递归地将中缀列表转换为前缀列表.我遇到如下问题:

;;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]
Run Code Online (Sandbox Code Playgroud)

问题出在哪儿?如何使用代码模板正确定义宏?

PS我把代码改成了这个,它有效,为什么?有什么不同?:

(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)))))
Run Code Online (Sandbox Code Playgroud)

ama*_*loy 13

在原始版本中,(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)))))
Run Code Online (Sandbox Code Playgroud)

但是,使用解构来提前提取表单的部分,而不是就地更好一点:

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

更好的是,真的,是将非递归情况移到外面,这样(a)它适用于文字数字,(b)代码看起来更像它扩展到:

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