lisp - 修改函数的参数

标签 lisp common-lisp

(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 对象(如字符串、向量,当然还有列表;事情稍微复杂一些,因为实现也可能有立即值,但那是旁边的重点在这里)。

seqsetf 修改函数的(私有(private)的,词法的)变量绑定(bind)。此更改在 removef 之外不可见。

为了removef能够在调用点影响周围的环境,你需要把它做成一个宏:

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

您可能想看看 setfgeneralized 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

似乎“有效”。从本质上讲,第一个示例没有按预期工作,因为函数 deleteremovef 的原始版本存在相同的问题,即它无法改变调用者对list 变量,所以即使是破坏性版本,正确的做法是:

(setf list (delete 1 (list 1 2 3 4)))

关于lisp - 修改函数的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17090429/

相关文章:

functional-programming - 寻找 Church 编码(lambda 演算)来定义 < , > , !=

macros - 如何检查符号是否为宏

lisp - 看到的scheme函数是ends match

lisp - 合并 2 个排序列表

common-lisp - 为什么 common lisp 的 case 构造总是匹配 True (T)?

lisp - 在 Common Lisp 中确定函数参数列表

lisp - 如何编译使用 cl-ppcre 的 clisp 程序?

variables - 无法在本地声明常量变量 在 Ubuntu Trusty Tahr 上安装人工智能现代方法代码时出错 - Common Lisp

lisp - SBCL 多线程写入标准输出

if-statement - `if` 的替代实现 - 难以理解的行为