将参数传递给语法引用的宏

时间:2017-02-21 14:06:10

标签: clojure macros

在我们的一个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)))

但是由于在评估之前宏的输出被插入到另一个代码块中,所以这是不可能的。

1 个答案:

答案 0 :(得分:2)

表单在传递给宏之前不会被评估,因为它们与函数一起使用。如果没有更广泛的背景或知道你正在尝试做什么,最简单的解决方案就是使用一个函数,因为你似乎想要在所有情况下评估参数,尽管我可能会过度简化你的问题:

(defn argtest [arg1 arg2]
  (str (arg1 testargs) " " (arg2 testargs)))