(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 中,参数是“按身份”传递的(this term goes back to D. Rettig,Allegro Common Lisp 实现的开发人员之一)。想想指针(指向堆对象)通过值传递,这对于大多数 Lisp 对象(如字符串、向量,当然还有列表;事情稍微复杂一些,因为实现也可能有立即值,但那是旁边的重点在这里)。
seq
的setf
修改函数的(私有(private)的,词法的)变量绑定(bind)。此更改在 removef
之外不可见。
为了removef
能够在调用点影响周围的环境,你需要把它做成一个宏:
(defmacro removef (element place)
`(setf ,place (remove ,element ,place)))
您可能想看看 setf
和 generalized references 的概念.请注意,我上面提供的 removef
宏版本不是实际应该如何完成!详情请阅读 get-setf-expansion
及其丑陋的细节。
如果您只想破坏性地修改列表,请考虑使用 delete
而不是 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
与 removef
的原始版本存在相同的问题,即它无法改变调用者对list
变量,所以即使是破坏性版本,正确的做法是:
(setf list (delete 1 (list 1 2 3 4)))
关于lisp - 修改函数的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17090429/