【问题标题】:passing arguments into syntax-quoted macros将参数传递给语法引用的宏
【发布时间】: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)。似乎有些人确实太喜欢他们的宏了。

标签: clojure macros


【解决方案1】:

与函数一样,表单在传递给宏之前不会被评估。在没有更广泛的背景或不知道您要做什么的情况下,最简单的解决方案是使用函数,因为您似乎希望在所有情况下都评估参数,尽管也许我过于简化了您的问题:

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

【讨论】:

  • 如果我会编写原始代码,我会选择这种方法,但作者选择使用宏来使用存储在映射中的引用列表生成代码,并将生成的代码插入到线程宏......我已经尝试解开混乱大约 6 个小时,并按照这里的建议用不到一个简单的函数映射替换了整个事情。非常感谢大家!
猜你喜欢
  • 2018-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多