如何解决为什么用这种方法将列表变异为它的第一个元素在 Common Lisp 中不起作用?
我正在尝试通过Common Lisp:符号计算的温和介绍这本书来学习 Common Lisp。此外,我正在使用 SBCL、Emacs 和 Slime。
到第 10 章结束时,在高级部分有这个问题:
10.9。编写一个破坏性函数 CHOP,将任何非 NIL 列表缩短为一个元素的列表。 (CHOP '(FEE FIE FOE FUM)) 应该返回 (收费)。
这是答题纸解决方案:
(defun chop (x)
(if (consp x) (setf (cdr x) nil))
x)
我理解这个解决方案。但是,在查看官方解决方案之前,我尝试了:
(defun chop (xs)
(cond ((null xs) xs)
(t (setf xs (list (car xs))))))
将其用作测试的全局变量:
(defparameter teste-chop '(a b c d))
我尝试过 REPL:
CL-USER> (chop teste-chop)
(A)
如您所见,该函数返回预期结果。
不幸的是,改变原始列表的副作用不会发生:
CL-USER> teste-chop
(A B C D)
为什么它没有改变?
由于我将整个列表的字段 (setf) 设置为仅将其汽车包装为新列表,因此我期望原始列表的 cdr 消失。
由于我在低级内容(如指针)方面存在很大差距,我认为这个问题的一些答案可以让我了解为什么会发生这种情况。
解决方法
重点是关于如何在 Common Lisp 中传递函数的参数。它们通过值传递。这意味着,当一个函数被调用时,所有的参数都会被计算,并且它们的值被分配给new、局部变量、函数的参数。因此,请考虑您的功能:
(defun chop (xs)
(cond ((null xs) xs)
(t (setf xs (list (car xs))))))
当你调用它时:
(chop teste-chop)
teste-chop
的值,即列表(a b c d)
被分配给函数参数xs
。在函数体的最后一行,通过使用 setf
,您将一个新值 (list (car xs))
分配给 xs
,即您将列表 (a)
分配给这个局部变量。
由于这是函数的最后一个表达式,因此函数也返回该值,因此 (chop test-chop)
的求值返回值 (a)
。
在这个过程中,正如你所看到的,特殊变量teste-chop
除了在函数调用的评估开始时计算它的值外,没有任何其他的关注。因此它的值没有改变。
其他语言中使用了其他形式的参数传递,例如按名称,因此函数调用的行为可能会有所不同。
请注意,在第一个函数中,使用 (setf (cdr x) nil)
修改了数据结构,它是 cons 单元的一部分。由于全局变量绑定到该单元格,因此全局变量也将显示为已修改(即使在某种意义上,它没有被修改,因为它仍然绑定到 相同 cons 单元格)。
最后要说的是,在 Common Lisp 中最好不要修改常量数据结构(例如通过评估 '(a b c d)
获得的数据结构),因为它可能会产生未定义的行为,具体取决于实现。因此,如果某些结构应该是可修改的,则应该使用诸如 cons
或 list
(例如 (list 'a 'b 'c 'd)
)之类的常用运算符来构建它。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。