【发布时间】:2017-02-21 14:06:10
【问题描述】:
在我们的一个 clojure 模块中寻找错误时,我偶然发现了一个语法引用的宏,有人向该宏传递参数。经过一些调试后,我意识到这不能按预期工作,因为参数没有它们应该具有的值。
下面是实际代码中遇到的情况的最小表示。它应该很好地说明了这一点:
(def testargs {:arg1 "foo"
:arg2 "bar"})
(defmacro argtest
[arg1 arg2]
`(str ~@(arg1 testargs) " " ~@(arg2 testargs))
如果我这样调用代码,这将按预期工作得很好:
(argtest :arg1 :arg2)
=> "foo bar"
但是,如果将两个参数作为变量传入,效果就不那么好了:
(let [arg1 :arg1 arg2 :arg2]
(argtest arg1 arg2))
=> " "
为了找到问题的根源,我将宏更改如下并再次运行测试:
(defmacro argtest2
[arg1 arg2]
(str arg1 " " arg2))
(argtest2 :arg1 :arg2)
=> ":arg1 :arg2"
(let [argument1 :arg1 argument2 :arg2]
(argtest2 argument1 argument2))
=> "argument1 argument2"
如您所见,发生的情况是变量 names 作为值传递。我怎样才能避免这种情况?我应该尝试重写代码以避免宏,还是可以以某种方式使其工作?
一个明显的解决方案当然是这样的:
(defmacro argtest
[arg1 arg2]
`(str (~arg1 ~testargs) " " (~arg2 ~testargs)))
但是由于宏的输出在求值之前被插入到另一个代码块中,这是不可能的。
【问题讨论】:
-
~testargs->testargs应该可以正常工作吗? -
(1) 如果你可以把宏改成函数,那就去做吧。 (2) 如果没有,请按照 cfrick 所说的使用
~而不是~@,从您所展示的内容来看,这似乎是不需要的。 -
@cfrick:是的,这适用于我在此处发布的内容,但生成的代码稍后会插入到线程宏中,这会开始大量抛出异常...
-
@Alan Thompson:谢谢,我选择了(1)。似乎有些人确实太喜欢他们的宏了。