In clojure, how to do code templating when implementing a macro using recursion

我的梦境 提交于 2020-01-14 09:16:50

问题


I am trying to implement a macro to recursively converting an infix list into a prefix one. I encounter a problem as follows:

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

Where is the problem? How to correctly define a macro with code templating?

P.S. I changed the code into this and it works, why? what is the difference?:

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


In the original version, the (if (not ...)) check was happening at compile-time; you've instead included it in the expanded code. So here's a minimal change that would get it to act like you want - it's effectively the same as the original, but "flipping" what's quoted and what's not.

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

However, it's a bit nicer to use destructuring to pull out the pieces of the form ahead of time, rather than in-place:

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

And better still, really, is to move the non-recursive case outside, so that (a) it works for literal numbers, and (b) the code looks more like what it expands to:

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


来源:https://stackoverflow.com/questions/8622652/in-clojure-how-to-do-code-templating-when-implementing-a-macro-using-recursion

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!