为什么elisp局部变量在这种情况下保持其值? [英] Why does an elisp local variable keep its value in this case?

查看:123
本文介绍了为什么elisp局部变量在这种情况下保持其值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人可以向我解释这个非常简单的代码片段中发生了什么?

 (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 of test-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't test-a and test-b equivalent?

解决方案

The Bad

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))

The Good

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++.

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) yields x. 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 or cons or copy-list instead of quote.

See more examples.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆