说我有以下代码:
(defmacro test1 [x]
(list 'fn '[y]
(if (pos? x)
'(println y)
'(println (- y)))))
它做我需要的,组成一个基于x的函数,并且不留下对x的引用。例如,(test1 1)
宏扩展为(fn* ([y] (println y)))
。
现在,我想用语法引用重写它。这就是我到目前为止所做的:
(defmacro test2 [x]
`(fn [y#]
(if ~(pos? x)
(println y#)
(println (- y#)))))
这完全相同,但有一个例外:它在扩展表达式中留下(if true ..)
表达式:
(fn* ([y__12353__auto__]
(if true
(clojure.core/println y__12353__auto__)
(clojure.core/println (clojure.core/- y__12353__auto__)))))
如果编译器可以优化它,这可能不是问题。不过,有没有办法可以省略它?
答案 0 :(得分:2)
当你使用test2
时,它将取消引用整个格式(pos? x)
,它将在编译时工作,如果它是一个常数或者已经定义的gloabl,但是如果你传递一个词法范围的变量名称尚不存在。
因此,你真的想要这个:
(defmacro test2 [x]
`(fn [y#]
(if (pos? ~x) ; just unquote x, not the whole predicate expression
(println y#)
(println (- y#)))))
(macroexpand '(test2 y))
; ==>
; (fn* ([y__1__auto__]
; (if (clojure.core/pos? y)
; (clojure.core/println y__1__auto__)
; (clojure.core/println (clojure.core/- y__1__auto__)))))
(defn test-it []
(let [y -9]
(test2 y)))
((test-it) 5) ; prints "-5"
随意使用您的版本试试这个。 (提示:由于 clojure.lang.Symbol无法转换为java.lang.Number ),您将收到异常
<强>更新强>
由于您希望基于常量来创建函数,因此需要稍微改写它:
(defmacro test3 [x]
(assert (number? x) "needs to be a compile time number")
(if (pos? x)
`(fn [y#] (println y#))
`(fn [y#] (println (- y#)))))
现在,如果您使用(test3 x)
,则会出现错误,因为x
不是数字,但在评估(test3 -10)
时得到您想要的内容,因为-10
是一个数字我们可以使用编译时间。我不确定你会注意到速度的提高,因为这些算法并不重要。