在Clojure中,使用递归实现宏时如何进行代码模板化 [英] In clojure, how to do code templating when implementing a macro using recursion
问题描述
我正在尝试实现一个宏,以将一个中缀列表递归转换为一个前缀列表.我遇到如下问题:
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.我将代码更改为此并且可以正常工作,为什么?有什么区别?:
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)))))
推荐答案
在原始版本中,(if (not ...))
检查是在编译时进行的.您已将其包含在扩展代码中.因此,这是一个最小的更改,可以使它按您想要的方式工作-与原始内容实际上是相同的,只是翻转"了引用的内容和没有的内容.
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)))))
更好的是,实际上是将非递归大小写移到外部,以便(a)适用于文字数字,并且(b)代码看起来更像是扩展为:
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)))))
这篇关于在Clojure中,使用递归实现宏时如何进行代码模板化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!