局部变量保持其值

局部变量保持其值

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

问题描述

谁能向我解释一下这个非常简单的代码片段发生了什么?

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

在第一次调用 (test-a) 时,我得到了预期的结果:((1)).但令我惊讶的是,再次调用它,我得到 ((1 1))((1 1 1)) 等等.为什么会这样?我期望 (test-a) 总是返回 ((1)) 是错误的吗?还要注意,重新计算test-a的定义后,返回结果重置.

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) 总是返回 ((1)).为什么 test-atest-b 不是等价的?

(test-b) always returns ((1)).Why aren't test-a and test-b equivalent?

推荐答案

The Bad

test-a自修改代码.这极其危险.虽然变量 xlet 形式的末尾消失了,但它的初始值 仍然存在于函数对象中,这就是您正在修改的值.请记住,在 Lisp 一个函数是第一类对象,它可以被传递(就像数字或列表一样),有时还修改.这正是您在这里所做的: x 的初始值是函数对象的一部分,您正在修改它.

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

优点

test-b 返回一个新的 cons 单元格,因此是安全的.x 的初始值永远不会被修改.(setcar x ...)(setq x ...) 的区别在于前者修改了已经存储在变量 x 而后者在 x存储一个 new 对象.区别类似于C++中的x.setField(42) vs. x = new MyObject(42).

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

一般来说,最好对待引用'(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) 相同:前者没有缺点.引用应该保留给永远不会被副作用修改的常量,除非你喜欢自修改代码.查看信息中的常见陷阱节点 重排 意外结果示例什么时候引用的对象被修改.

如果您需要修改列表,使用 listconscopy-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.

这篇关于在这种情况下,为什么 elisp 局部变量保持其值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 15:46