更新时间:2022-06-23 23:11:07
test-a
是自修改代码.这极其危险.虽然变量 x
在let
形式的末尾消失了,但它的初始值 仍然存在于函数对象中,这就是您正在修改的值.请记住,在 Lisp 一个函数是第一类对象,它可以被传递(就像数字或列表一样),有时还修改.这正是您在这里所做的: x
的初始值是函数对象的一部分,您正在修改它.
test-a
is self-modifying code. This is extremely dangerous. While the variable x
disappears at the end of the let
form, its initial value persists in the function object, and that is the value you are modifying. Remember that in Lisp a function is a first class object, which can be passed around (just like a number or a list), and, sometimes, modified. This is exactly what you are doing here: the initial value for x
is a part of the function object and you are modifying it.
让我们实际看看发生了什么:
Let us actually see what is happening:
(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
中存储一个 new 对象.区别类似于C++
中的x.setField(42)
vs. x = new MyObject(42)
.
test-b
returns a fresh cons cell and thus is safe. The initial value of x
is never modified. The difference between (setcar x ...)
and (setq x ...)
is that the former modifies the object already stored in the variable x
while the latter stores a new object in x
. The difference is similar to x.setField(42)
vs. x = new MyObject(42)
in C++
.
一般来说,***对待引用 像 '(1)
这样的数据作为常量 - 不要不要修改它们:
In general, it is best to treat quoted data like '(1)
as constants - do not modify them:
quote
返回参数,而不计算它.(quote x)
产生 x
.警告:quote
不构造它的返回值,而只是返回由 Lisp 阅读器预先构造的值(请参阅信息节点印刷表示).这意味着 (a . b)
不是与 (cons 'a 'b)
相同:前者没有缺点.引用应该保留给永远不会被副作用修改的常量,除非你喜欢自修改代码.查看信息中的常见陷阱节点 重排 意外结果示例什么时候引用的对象被修改.
quote
returns the argument, without evaluating it.(quote x)
yieldsx
. Warning:quote
does not construct its return value, but just returns the value that was pre-constructed by the Lisp reader (see info node Printed Representation). This means that(a . b)
is not identical to(cons 'a 'b)
: the former does not cons. Quoting should be reserved for constants that will never be modified by side-effects, unless you like self-modifying code. See the common pitfall in info node Rearrangement for an example of unexpected results when a quoted object is modified.
如果您需要修改列表,使用 list
或 cons
或 copy-list
而不是 quote
创建它.
If you need to modify a list, create it with list
or cons
or copy-list
instead of quote
.
附注.这已在 Emacs 上重复.
PS. This has been duplicated on Emacs.
PPS.另请参阅为什么此函数每次都返回不同的值?对于相同的 Common Lisp 问题.
PPS. See also Why does this function return a different value every time? for an identical Common Lisp issue.