Macros 在宏上取消引号[&;args]

Macros 在宏上取消引号[&;args],macros,clojure,Macros,Clojure,如果我想定义一个接受未知数量参数的函数,我可以很容易地执行以下操作: (defn foo [& args] args) (foo "foo" 1 2 3 4) 它返回类型为clojure.lang.ArraySeq的值,其中包含函数中给定的值 在宏中,使用~@是取消报价拼接,这会展平给定的参数: (defmacro foo [bar & baz] `(println ~@baz)) 如果我像这样使用宏: (defn foo [& args]

如果我想定义一个接受未知数量参数的函数,我可以很容易地执行以下操作:

(defn foo
  [& args]
  args) 
(foo "foo" 1 2 3 4)
它返回类型为
clojure.lang.ArraySeq
的值,其中包含函数中给定的值

在宏中,使用
~@
是取消报价拼接,这会展平给定的参数:

(defmacro foo
  [bar & baz]
  `(println ~@baz))
如果我像这样使用宏:

(defn foo
  [& args]
  args) 
(foo "foo" 1 2 3 4)
它将打印出
1234
。但是,我想让ARG们放松。做
~baz
(没有
@
)会给我一个例外。你知道怎么做吗?

你总能做到

(defmacro foo
  [bar & baz]
  `(println (list ~@baz)))
构造一个seq或

(defmacro foo
  [bar & baz]
  `(println [~@baz]))

构造一个向量

如果您使用
macroexpand-1
使用
~baz
的版本,则问题的原因显而易见:

(macroexpand-1 '(foo "foo" 1 2 3 4)) ; NB. using ~baz in foo's body
;= (println (1 2 3 4))
;            ^
因此,这里我们试图将
1
作为函数调用。(注意:此调用发生,并且在运行时引发异常。)

解决这一问题的正确方法取决于您想要实现的目标。例如,您可以将剩余参数倒入向量并打印:

(defmacro foo [bar & baz]
  `(println ~(vec baz)))

对MichałMarczyk回答的评论

通过将宏转换为函数,您还可以看到发生了什么:

(defn foo [bar & baz]
  `(println ~@baz))

=> (foo "foo" 1 2 3 4)
(clojure.core/println (1 2 3 4))

语法引用、取消引用和取消引用拼接仍然有效。啊哈!宏与函数的不同之处在于调用它们的方式,而不是它们的作用

感谢您提及
macroexpand-1