Macros 写入“循环”时出现的问题。。。宏中的collect`

Macros 写入“循环”时出现的问题。。。宏中的collect`,macros,lisp,common-lisp,clisp,Macros,Lisp,Common Lisp,Clisp,伙计们 今天我想写sigma宏来计算灵活表达式输入的和 下面的代码是我今天下午编写的。但这不符合我的目的 (defmacro sigma (exp ll) `(+ ,@(loop for i in ll collect (progn (setf (elt exp 1) i) (print exp) exp))) ) >>(pprint (macroexpand-1 '(

伙计们

今天我想写sigma宏来计算灵活表达式输入的和

下面的代码是我今天下午编写的。但这不符合我的目的

(defmacro sigma (exp ll)
  `(+ ,@(loop for i in ll collect
             (progn (setf (elt exp 1) i)
                    (print exp)
                    exp)))
  )

>>(pprint (macroexpand-1 '(sigma (+ 1 2) (2 3 4))))
>>(+ 2 2) 
  (+ 3 2) 
  (+ 4 2) 
  (+ (+ 4 2) (+ 4 2) (+ 4 2))
我希望它能工作
(+(+22)(+32)(+42))
但是
循环收集
给我一个奇怪的答案


为什么它是这样工作的?我有一些方法来解决这个问题吗?

您正在修改文字数据(引用)。如果您同意在该循环期间,列表
(+12)
,绑定到exp,在每次迭代中都是相同的,并且在每次迭代中都会对第二个元素进行变异。很容易想象,一个已经收集了相同列表
exp
3次的列表将有3个与第二个元素的最后一次变异完全相同的元素

这绝不是宏中的一项功能。对所有引用的数据进行变异可以产生这样的结果。标准规定结果将是未定义的,因此没有实现者需要解决这个问题,并且您可以从特定实现的其他方面获得意外行为

编译后的文件可能会将所有引用的数据连接在一起,成为一个相同的数据,这样代码中的其他位置也可能受到此宏的影响

要解决此问题,请不要突变:

(defmacro sigma ((op r &rest rest) ll)
  `(+ ,@(loop :for i :in ll 
              :collect (list* op i rest))))

(macroexpand-1 '(sigma (+ 1 2) (2 3 4)))
; ==> (+ (+ 2 2) (+ 3 2) (+ 4 2))
这样做的好处是保证模板中至少有两个参数

(macroexpand-1 '(sigma (x) (2 3 4)))
; ==> *** - SIGMA: (X) does not match lambda list element (OP R &REST REST)

如果您想要一份新消耗的列表,那么
复制列表
是一种方法:

(defmacro sigma (exp ll)
  `(+ ,@(loop for i in ll and exp1 = (copy-list exp)
              do (setf (second exp1) i)
              collect exp1)))
也可以使用嵌套的反引号表达式:

(defmacro sigma ((op arg0 &rest args) ll)
  (declare (ignore arg0))
  `(+ ,@(loop for i in ll collect `(,op ,i ,@args))))

即使他没有对它进行变异,他每次都通过循环收集相同的
exp
对象。@Barmar我记得我有一次
ncoc
-对同一个参数进行了两次修改,得到了一个双倍大小的列表:-)