I'm trying (as a self-learning exe开发者_JAVA百科rcise) to create a Clojure macro that will generate code to apply a function to a sequence of integers and sum the result, e.g.
f(0) + f(1) + f(2) + f(3)
This is my attempt:
(defmacro testsum [func n]
`(fn [x#] (+ ~@( map (fn [i] `(~func x#)) (range n)))))
However something seems to go wrong with the x# gensym and I end up with two different versions of x and hence the function doesn't work:
(macroexpand '(testsum inc 3))
gives:
(fn* ([x__809__auto__]
(clojure.core/+
(inc x__808__auto__)
(inc x__808__auto__)
(inc x__808__auto__))))
This is pretty much exactly what I want apart from the different 809 and 808 versions of x.....
What am I doing wrong? I thought that the auto gensym was meant to create a single unique symbol for exactly this kind of purpose? Is there a better way of doing this?
foo#
-style gensyms are valid only inside the syntax-quote where they were created. In your code, the two x#
s are created in different syntax-quote blocks:
(defmacro testsum [func n]
`(fn [x#] (+ ~@( map (fn [i] `(~func x#)) (range n)))))
^- s-q1 ^-unquote ^- s-q2
To fix this, use an explicit (gensym)
call:
(defmacro testsum [func n]
(let [x (gensym "x")]
`(fn [~x] (+ ~@(map (fn [i] `(~func ~x)) (range n))))))
And the macro expansion ((macroexpand '(testsum inc 3))
):
(fn* ([x4966] (clojure.core/+ (inc x4966) (inc x4966) (inc x4966))))
精彩评论