Common lisp 为什么用这种方法将列表变为它的第一个元素在CommonLisp中不起作用?
我试图通过《CommonLisp:符号计算的温和介绍》一书来学习CommonLisp。此外,我正在使用SBCL、Emacs和Slime 在第10章末尾,关于高级部分,有一个问题: 10.9。写一个破坏性函数印章,将任何非零列表缩短为一个元素列表。(盖章)(费用为5美元)应退还 (费用) 这是答案表解决方案:Common lisp 为什么用这种方法将列表变为它的第一个元素在CommonLisp中不起作用?,common-lisp,variable-assignment,in-place,mutability,Common Lisp,Variable Assignment,In Place,Mutability,我试图通过《CommonLisp:符号计算的温和介绍》一书来学习CommonLisp。此外,我正在使用SBCL、Emacs和Slime 在第10章末尾,关于高级部分,有一个问题: 10.9。写一个破坏性函数印章,将任何非零列表缩短为一个元素列表。(盖章)(费用为5美元)应退还 (费用) 这是答案表解决方案: (defun chop (x) (if (consp x) (setf (cdr x) nil)) x) 我理解这个解决方案。但是,在查看官方解决方案之前,我尝试了: (de
(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消失
显然,指针不会自动删除
因为我在低级的东西(比如指针)上有很大的差距,所以我认为这个问题的一些答案可以告诉我为什么会发生这种情况。重点是关于如何在公共Lisp中向函数传递参数。它们是按值传递的。这意味着,当调用函数时,所有参数都将被求值,它们的值将被分配给新的局部变量,即函数的参数。所以,考虑一下你的功能:
(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
,也就是说,您将list(a)
分配给这个局部变量
由于这是函数的最后一个表达式,该函数也会返回该值,因此对(chop test chop)
的求值将返回值(a)
在这个过程中,您可以看到,除了在函数调用的求值开始时计算其值外,特殊变量teste chop
与此无关。由于这个原因,它的值没有改变
其他语言中使用了其他形式的参数传递,例如按名称传递,因此函数调用的行为可能会有所不同
请注意,在第一个函数中,使用(setf(cdr x)nil)
修改了数据结构,这是cons单元的一部分。由于全局变量绑定到该单元格,因此全局变量也将显示为已修改(即使在某种意义上它未被修改,因为它仍然绑定到同一单元格)
最后,在Common Lisp中,最好不要修改常量数据结构(如通过计算
”(a b c d)
)获得的数据结构),因为它可能会产生未定义的行为,具体取决于实现。因此,如果某些结构应该是可修改的,那么它应该使用常见的操作符来构建,如cons
或list
(例如(list'a'b'c'd)
)。。。或者通常构建,这是最方便的,然后通过复制列表
。setf是“set place”<代码>(setf(cdr-xs)…。设置(更改)位置(cdr-xs)
(setf xs…
设置位置xs
——这是函数的绑定xs
,而不是它绑定到的值setf
ing函数的形式参数永远不会影响该函数之外的任何内容。