我似乎可以在Clojure中围绕宏。我可能遗漏了一些基本的东西。首先,请允许我描述一下我想要的例子。
(defmacro macrotest [v] `(d ...))
(def a '(b c))
(macroexpand-1 '(macrotest a))
; => (d (b c))
换句话说,传递给var
的{{1}}已解决,但未进一步评估。
为宏提供var的值:
macrotest
但是提供var不会:
(defmacro macrotest [v] `(d ~v))
(macroexpand-1 '(macrotest (b c)))
; => (d (b c))
是否可以在Clojure宏中解析 (def a '(b c))
(macroexpand-1 '(macrotest a))
; => (d a)
,但不能评估其值?
编辑:var
:
eval
答案 0 :(得分:2)
了解宏在编译时评估非常重要,而不是在运行时。
在编译时var a
还没有值,因此它的值不会传递给宏,只传递给它的名称。但是,当您显式传递“值”时 - 那么它在编译时存在,并且您看到它“有效”。
答案 1 :(得分:2)
在宏中使用eval
是不好的做法。当然,以下工作:
(macroexpand-1 '(macrotest a))
; => (user/d (b c))
...但是,这不能按预期工作:
(macroexpand-1 '(let [a "oh no"]
(macrotest a)))
; => (user/d (b c))
当然,您希望宏评估a
,因为它是在本地定义的,而不是使用绑定到它的全局变量,但是您不能,因为宏不会在正确的词汇上下文中评估v
。这是理解宏的关键:它们采用代码并生成代码;您尚未使用该代码操纵的任何数据。换句话说,扩展宏的时刻可能与它们生成的代码的计算时间完全无关:在执行宏时评估的任何内容都可能过早评估w.r.t.您的数据的相关性。
有一个名为macrotest
的表单应接受参数v
,然后执行 - 如果您已应用表单d
。但是:
(macrotest (b c))
不应评估(b c)
,只需将其复制即可生成(d (b c))
。(macrotest a)
应评估a
,这会生成引用的表单,并将该表单放在生成的代码中,同时返回(d (b c))
。这里有一个问题,因为在一种情况下,含义会发生根本变化。要么你评估一个参数,在这种情况下你必须引用(b c)
并写下:
(defmacro macrotest [v] `(d ~v))
...要求d
准备处理引用的参数;或者你对没有评估的论点没问题。
使用与上面相同的d
,您可以明确引用该参数:
(defmacro macrotest [v] `(d '~v))
但是,如果d
本身是一个不评估其参数的宏,则必须避免在~v
前面引用该引号。