人
今天我想编写sigma宏来计算灵活表达式输入的总和。
以下代码是我今天下午写的。但它不符合我的目的。
sudo
我希望它有效(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))
,但(+ (+ 2 2) (+ 3 2) (+ 4 2))
给了我奇怪的答案。
为什么它会像这样工作?我有一些方法可以解决这个问题吗?
答案 0 :(得分:6)
您正在改变文字数据(引用)。如果您同意在该循环期间,绑定到exp的列表(+ 1 2)
在每次迭代中是相同的,并且您在每次迭代中改变第二个元素,很容易想象收集的列表相同的列表exp
3次将具有3个完全相同的元素,其中第二个元素的最后一个突变。
这绝不是宏中的一项功能。对所有引用数据进行变异可以产生这样的结果。标准规定结果将是未定义的,因此没有实现者需要解决这个问题,并且您会从特定实现的其他方面获得意外行为。
编译后的文件可能会将所有引用的数据合并为一个并且相同,这样代码中的'(+ 1 2)
个其他位置也可能会受到此宏的影响。
要解决这个问题,请不要改变:
(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)
答案 1 :(得分:3)
如果你想要一个新的列表,那么copy-list
是一种方式:
(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))))