Clojure宏难题:在宏参数中展开序列

Clojure宏难题:在宏参数中展开序列,clojure,Clojure,这不是我的“生产代码”,而是为了说明问题而对问题的简化。此外,这个问题的标题是误导性的,因为它让我想起了~@扩展,我理解,这可能不一定是问题所在。如果可以,请建议一个更好的问题标题 给定具有以下形式的宏: (defmacro my-add [x & ys] `(+ ~x ~@ys)) 现在让我们假设我们有一个列表: (def my-lst '(2 3)) 现在我想要一个使用add的函数,我可以将我的lst作为arg传递给它 (call-my-add 1 my-lst) 我以一种显而

这不是我的“生产代码”,而是为了说明问题而对问题的简化。此外,这个问题的标题是误导性的,因为它让我想起了~@扩展,我理解,这可能不一定是问题所在。如果可以,请建议一个更好的问题标题

给定具有以下形式的宏:

(defmacro my-add [x & ys] `(+ ~x ~@ys))
现在让我们假设我们有一个列表:

(def my-lst '(2 3))
现在我想要一个使用add的函数,我可以将我的lst作为arg传递给它

(call-my-add 1 my-lst)
我以一种显而易见的方式定义函数:

(defn call-my-add [x ys]
    (apply my-add (cons x ys)))
但是:

我尝试了各种各样的技巧,使用evals、applications甚至将call my add定义为宏来让调用my add函数正常工作,但它们都给出了类似的ClassCastException


有办法解决这个问题吗

否。宏没有、不能、永远不会访问其参数中包含的实际运行时值,因此无法将它们拼接到扩展中。他们所得到的只是你传递给他们的符号,在本例中是
my list
。“解决方法”是将我的add定义为一个函数,然后(可选)有一个宏调用该函数以生成其代码

我最近写了一篇关于这个问题的文章,你可能会觉得很有启发性

如果愿意,您可以使用evals来实现,但在几乎所有情况下,这都是一个糟糕的想法:

(let [my-list '(1 2)]
  (eval `(my-add 5 ~@my-list)))

这是一个很好的例子,说明了在Clojure(或我所知道的任何Lisp)中,宏并不是一流的公民。它们不能应用于函数,不能存储在容器中,也不能传递给函数等。作为交换,它们可以控制何时以及是否计算参数。

在宏扩展时发生的事情必须保留在宏扩展时。因此,如果在宏扩展时正在评估
my add
,并且您想要使用
apply
,那么您需要。。。另一个宏观;做申请

(defmacro call-my-add [x ys]
   `(my-add ~@(cons x ys))) 
宏在某种程度上具有传染性


PS:我不在我的repl,所以如果您在本例中看到错误,请进行编辑(或者我回来后会修复它)

PSSST。。。宏可以很好地读取在宏扩展时传递给它们的参数,以澄清如果值传递给宏生成的任何代码(因为宏已经完成),它们无法读取的内容感谢amalloy。但我可以制作如下宏:(defmacro blah[x](let[y(eval x)]`(println~y)),它可以在模板化参数之前对参数求值。所以宏似乎可以访问这些值,对吗?这…有点正确,但我将其归类为误导。如果x恰好是一个顶级
def
'd变量或一个仅由此类变量组成的表达式,那么是的,它将起作用。但是在宏作用域中使用eval在大多数情况下都不起作用,因为它不能从调用作用域访问本地绑定。具体地说,
(让[x10](blah x))
无法与您的
blah
宏一起工作,因为调用
(eval'x)
时,
x
不在范围内。我不确定您在这里得到的是什么。这并不能解决OP的问题。(称为我的add1(35))将起作用,但这只是因为(35)是一个文字。如果您(定义我的列表’(35)),(调用我的添加1我的列表)将失败,因为我的列表(符号)不是序列。
(defmacro call-my-add [x ys]
   `(my-add ~@(cons x ys)))