微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么用这种方法将列表变异为它的第一个元素在 Common Lisp 中不起作用?

如何解决为什么用这种方法将列表变异为它的第一个元素在 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) 获得的数据结构),因为它可能会产生未定义的行为,具体取决于实现。因此,如果某些结构应该是可修改的,则应该使用诸如 conslist(例如 (list 'a 'b 'c 'd))之类的常用运算符来构建它。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。