为什么elisp局部变量在这种情况下保持其值? [英] Why does an elisp local variable keep its value in this case?
问题描述
(defun test-a()
(let((x'(nil)))
(setcar x(cons 1(car x)))
x))
首次调用(test-a)
时,我得到预期的结果:((1))
。
但令我惊讶的是,再次打电话给我,我得到((1 1))
,((1 1 1))
等等。
为什么会发生这种情况?我错误地想要(test-a)
始终返回((1))
?
另请注意,在重新评估 test-a
的定义后,返回结果将重置。
还要考虑这个函数的工作原理是我想要的:
(defun test-b()
$ ($)
(let x'(nil)))
(setq x(cons(cons 1(car x))
(cdr x)))))
(test-b)
总是返回((1))
。
为什么不是test-a
和test-b
等价?解决方案
坏
test-a
自修改代码。这是非常危险的。而在let
形式的末尾,变量x
消失,其初始值仍然存在于函数对象中,那就是要修改的值。请记住,在Lisp中一个函数是第一类对象,可以传递(就像一个数字或一个列表),有时,修改。这正是你在这里做的: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))
(符号函数'test-a)
=> (lambda nil(let((x(quote((1 1)))))(setcar x(cons 1(car x)))x))
(test-a)
=> ((1 1 1))
(符号函数'test-a)
=> (lambda nil(let((x(quote((1 1 1)))))(setcar x(cons 1(car x)))x))
Good
test-b
cons cell,因此是安全的。初始值x
永远不会被修改。(setcar x ...)
和(setq x ...)
之间的区别是前者修改对象已经存储在变量x
中,而对对象存储X
。差异与x.setField(42)
对x = new MyObject(42)
在C ++
。
底线
一般来说,最好处理引用的数据,如
'(1)
作为常量 - do 不修改它们:
quote
返回参数,而不进行评估。(报价x)
产生x
。
警告:quote
不构造其返回值,而只是返回
由Lisp阅读器(见信息节点
印刷代表)。这意味着(a。b)
不是
与(cons'a'b)
相同:前者并不失望引用应该为
保留永久不会被副作用修改的
,除非你喜欢自修改代码。请参阅info
节点中的常见陷阱重新排列的一个例子,当
a引用对象被修改时,会出现意外的结果。
如果你需要修改列表,使用<$ c $创建它c>列表或
cons
或copy-list
而不是报价
。
PS。这已在 Emacs 上重复。
PPS。另见为什么这个函数每次返回一个不同的值?相同的Common Lisp问题。
Could someone explain to me what's going on in this very simple code snippet?
(defun test-a () (let ((x '(nil))) (setcar x (cons 1 (car x))) x))
Upon a calling
(test-a)
for the first time, I get the expected result:((1))
. But to my surprise, calling it once more, I get((1 1))
,((1 1 1))
and so on. Why is this happening? Am I wrong to expect(test-a)
to always return((1))
? Also note that after re-evaluating the definition oftest-a
, the return result resets.Also consider that this function works as I expect:
(defun test-b () (let ((x '(nil))) (setq x (cons (cons 1 (car x)) (cdr x)))))
(test-b)
always returns((1))
. Why aren'ttest-a
andtest-b
equivalent?解决方案The Bad
test-a
is self-modifying code. This is extremely dangerous. While the variablex
disappears at the end of thelet
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 forx
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))
The Good
test-b
returns a fresh cons cell and thus is safe. The initial value ofx
is never modified. The difference between(setcar x ...)
and(setq x ...)
is that the former modifies the object already stored in the variablex
while the latter stores a new object inx
. The difference is similar tox.setField(42)
vs.x = new MyObject(42)
inC++
.The Bottom Line
In general, it is best to treat quoted data like
'(1)
as constants - do not modify them:
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.If you need to modify a list, create it with
list
orcons
orcopy-list
instead ofquote
.PS. This has been duplicated on Emacs.
PPS. See also Why does this function return a different value every time? for an identical Common Lisp issue.
这篇关于为什么elisp局部变量在这种情况下保持其值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!