【问题标题】:Controlling symbol generation in Clojure macros在 Clojure 宏中控制符号生成
【发布时间】:2011-02-19 20:56:55
【问题描述】:

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

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

这是我的尝试:

(defmacro testsum [func n] 
  `(fn [x#] (+ ~@( map (fn [i] `(~func x#)) (range n)))))

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

(macroexpand '(testsum inc 3))

给予:

(fn* ([x__809__auto__] 
  (clojure.core/+ 
    (inc x__808__auto__) 
    (inc x__808__auto__) 
    (inc x__808__auto__))))

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

我做错了什么?我认为 auto gensym 旨在为这种目的创建一个独特的符号?有更好的方法吗?

【问题讨论】:

    标签: code-generation clojure macros symbols


    【解决方案1】:

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

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

    要解决此问题,请使用明确的(gensym) 调用:

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

    以及宏扩展((macroexpand '(testsum inc 3))):

    (fn* ([x4966] (clojure.core/+ (inc x4966) (inc x4966) (inc x4966))))
    

    【讨论】:

    • 事后考虑,也可以将(gensym "x") 替换为`x#,尽管我从未见过有人为在主要扩展生成形式之外显式创建的gensym 这样做。跨度>
    • 真棒 Michal - 非常感谢!来自 Java 世界后,我仍然对符号感到头疼....
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-05
    • 1970-01-01
    相关资源
    最近更新 更多