控制Clojure宏中的符号生成

mik*_*era 19 macros code-generation symbols clojure

我正在尝试(作为一种自学练习)创建一个Clojure宏,它将生成代码以将函数应用于整数序列并对结果求和,例如

f(0)+ f(1)+ f(2)+ f(3)

这是我的尝试:

(defmacro testsum [func n] 
  `(fn [x#] (+ ~@( map (fn [i] `(~func x#)) (range n)))))
Run Code Online (Sandbox Code Playgroud)

然而,x#gensym似乎出现了问题,我最终得到了两个不同版本的x,因此该函数不起作用:

(macroexpand '(testsum inc 3))
Run Code Online (Sandbox Code Playgroud)

得到:

(fn* ([x__809__auto__] 
  (clojure.core/+ 
    (inc x__808__auto__) 
    (inc x__808__auto__) 
    (inc x__808__auto__))))
Run Code Online (Sandbox Code Playgroud)

除了不同的809和808版本的x之外,这几乎是我想要的......

我究竟做错了什么?我认为自动gensym旨在为这种目的创造一个独特的符号?有没有更好的方法呢?

Mic*_*zyk 24

foo#样式gensyms仅在创建它们的语法引号内有效.在您的代码中,两个x#s是在不同的语法 - 引用块中创建的:

(defmacro testsum [func n] 
  `(fn [x#] (+ ~@( map (fn [i] `(~func x#)) (range n)))))
  ^- s-q1      ^-unquote       ^- s-q2
Run Code Online (Sandbox Code Playgroud)

要解决此问题,请使用显式(gensym)调用:

(defmacro testsum [func n]
  (let [x (gensym "x")]
    `(fn [~x] (+ ~@(map (fn [i] `(~func ~x)) (range n))))))
Run Code Online (Sandbox Code Playgroud)

而宏扩展((macroexpand '(testsum inc 3))):

(fn* ([x4966] (clojure.core/+ (inc x4966) (inc x4966) (inc x4966))))
Run Code Online (Sandbox Code Playgroud)

  • 作为事后的想法,也可以将`(gensym"x")`替换为`\`x#`,尽管我从未见过有人为主要扩展生成形式之外明确创建的gensym做过这样的事情. (3认同)