[英]Clojure macros: resolving vars
我似乎可以将头放在Clojure中的宏周围。 我可能缺少一些基本的知识。 首先,让我描述一个我想要的例子。
(defmacro macrotest [v] `(d ...))
(def a '(b c))
(macroexpand-1 '(macrotest a))
; => (d (b c))
换句话说,传递给macrotest
的var
已解析,但未进行进一步评估。
向宏提供var的值的工作原理:
(defmacro macrotest [v] `(d ~v))
(macroexpand-1 '(macrotest (b c)))
; => (d (b c))
但是提供var不会:
(def a '(b c))
(macroexpand-1 '(macrotest a))
; => (d a)
是否可以在Clojure宏中解析var
,但不评估其值?
编辑:我想要什么似乎可以与eval
:
(defmacro macrotest [v] `(d ~(eval v)))
(def a '(b c))
(macroexpand-1 '(macrotest a))
; => (user/d (b c))
重要的是要了解宏在编译时而不是在运行时求值 。
在编译时, var a
还没有值,因此其值不会传递给宏,而仅传递给其名称。 但是,当您显式传递“值”时,那么它在编译时就存在,并且您会看到它“起作用”。
在宏内使用eval
是不好的做法。 当然,可以进行以下工作:
(macroexpand-1 '(macrotest a))
; => (user/d (b c))
...但是,这不能按预期工作:
(macroexpand-1 '(let [a "oh no"]
(macrotest a)))
; => (user/d (b c))
当然,您希望宏对a
本地定义的a
进行求值,而不使用绑定到全局变量的全局变量,但是您不能这样做,因为宏没有在正确的词法上下文中求v
。 这是理解宏的关键:它们获取代码并生成代码; 该代码处理的所有数据尚不可用。 换句话说,宏扩展的时间可能与结果代码的评估时间完全无关:在宏执行过程中评估的任何内容都可能与数据的相关性过早评估。
有一种名为macrotest
的形式,该形式应接受参数v
,然后如果您已将形式d
应用于它, macrotest
执行。 但:
(macrotest (bc))
不应评估(bc)
,只需将其原样复制即可产生(d (bc))
。 (macrotest a)
应评估a
,结果以引号引起来,并将该形式放入结果代码中,并返回(d (bc))
。 您在这里遇到了问题,因为在一种或另一种情况下含义会发生根本变化。 要么评估一个参数,在这种情况下,您必须引用(bc)
并编写:
(defmacro macrotest [v] `(d ~v))
...要求d
准备好处理引用的参数; 否则您可以接受不评估的参数。 使用与上面相同的d
,可以显式引用参数:
(defmacro macrotest [v] `(d '~v))
但是,如果d
本身是不计算其参数的宏,你必须避免在前面的报价~v
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.