坏的
test-a
是自修改代码。这是极其危险的。虽然变量 在表单x
末尾消失,但它的初始值仍然存在于函数对象中,这就是您正在修改的值。请记住,在 Lisp 中,函数是第一类对象,可以传递(就像数字或列表一样),有时也可以修改。这正是您在这里所做的:初始值是函数对象的一部分,您正在修改它。let
x
让我们实际看看发生了什么:
(symbol-function 'test-a)
=> (lambda nil (let ((x (quote (nil)))) (setcar x (cons 1 (car x))) x))
(test-a)
=> ((1))
(symbol-function 'test-a)
=> (lambda nil (let ((x (quote ((1))))) (setcar x (cons 1 (car x))) x))
(test-a)
=> ((1 1))
(symbol-function 'test-a)
=> (lambda nil (let ((x (quote ((1 1))))) (setcar x (cons 1 (car x))) x))
(test-a)
=> ((1 1 1))
(symbol-function 'test-a)
=> (lambda nil (let ((x (quote ((1 1 1))))) (setcar x (cons 1 (car x))) x))
好的
test-b
返回一个新的 cons 单元格,因此是安全的。的初始值x
永远不会被修改。(setcar x ...)
和之间的区别在于(setq x ...)
,前者修改了已经存储在变量中的对象x
,而后者将新对象存储在x
. 区别类似于x.setField(42)
vs. x = new MyObject(42)
in C++
。
底线
通常,最好将引用的数据'(1)
视为常量 - 不要修改它们:
quote
返回参数,而不评估它。 (quote x)
产量x
。
警告:quote
不构造它的返回值,而只是返回由 Lisp 阅读器预先构造的值(参见信息节点
Printed Representation)。这意味着(a . b)
不等同于(cons 'a 'b)
:前者没有缺点。引号应该保留给永远不会被副作用修改的常量,除非您喜欢自修改代码。有关修改引用对象时意外结果的示例,请参阅信息节点重新排列中的常见陷阱。
如果您需要修改列表list
,请使用orcons
或copy-list
代替 来创建它quote
。
查看更多 示例。
PS。这已在Emacs上复制。
聚苯乙烯。另请参阅为什么此函数每次都返回不同的值?对于相同的 Common Lisp 问题。