在我们的一个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"
如您所见,发生的变量是名称作为值传递。我怎么能避免这个?我是否应该尝试重写代码以避免使用宏,或者我可以以某种方式使其工作?
一个明显的解决方案当然是这样的:
(defmacro argtest
[arg1 arg2]
`(str (~arg1 ~testargs) " " (~arg2 ~testargs)))
但是由于在评估之前宏的输出被插入到另一个代码块中,所以这是不可能的。
答案 0 :(得分:2)
表单在传递给宏之前不会被评估,因为它们与函数一起使用。如果没有更广泛的背景或知道你正在尝试做什么,最简单的解决方案就是使用一个函数,因为你似乎想要在所有情况下评估参数,尽管我可能会过度简化你的问题:
(defn argtest [arg1 arg2]
(str (arg1 testargs) " " (arg2 testargs)))