(setf list (loop for i from 1 to 12 collect i))
(defun removef (item seq)
  (setf seq (remove item seq)))


CL-USER> (removef 2 list)
(1 3 4 5 6 7 8 9 10 11 12)

CL-USER> (removef 3 list)
(1 2 4 5 6 7 8 9 10 11 12)

为什么removef不真正修改变量?

最佳答案

在Common Lisp中,参数是“按标识”传递的(Allegro Common Lisp实现的开发人员之一)想想指针(到堆对象)是通过值传递的,这对大多数Lisp对象(比如字符串、向量,当然还有列表)都是正确的;事情稍微复杂一点,因为实现也可能有即时值,但这不是重点。
setfseq修改函数的(私有、词法)变量绑定。此更改在removef之外不可见。
为了使removef能够影响调用点的周围环境,您需要将其设置为宏:

(defmacro removef (element place)
   `(setf ,place (remove ,element ,place)))

你可能想看看this term goes back to D. Rettigsetf的概念注意,上面我提供的removef的宏版本并不是实际应该如何实现的!有关详细信息,请阅读generalized references及其难看的详细信息。
如果您只想破坏性地修改列表,请考虑使用get-setf-expansion而不是remove,但请注意,这可能会产生意想不到的后果:
(delete 2 '(1 2 3 4))

ANSI标准不允许(您正在破坏性地修改文本对象,即代码的一部分)在这个例子中,这个错误很容易被发现,但是如果您在某个调用堆栈中有7帧深,并且处理的值的原点对您来说并不完全清楚,那么这就成了一个真正的问题不管怎样,甚至
(setf list (list 1 2 3 4))
(delete 1 list)
list

一开始可能会很惊讶,尽管
(setf list (list 1 2 3 4))
(delete 2 list)
list

似乎是“工作”。基本上,第一个示例并没有按预期工作,因为函数delete与原始版本的delete有相同的问题,也就是说,它不能改变调用方对removef变量的概念,所以即使对于破坏性版本,正确的方法是:
(setf list (delete 1 (list 1 2 3 4)))

10-05 18:08