Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Emacs 在这种情况下,为什么elisp局部变量保持其值?_Emacs_Lisp_Elisp_Literals - Fatal编程技术网

Emacs 在这种情况下,为什么elisp局部变量保持其值?

Emacs 在这种情况下,为什么elisp局部变量保持其值?,emacs,lisp,elisp,literals,Emacs,Lisp,Elisp,Literals,有人能给我解释一下这个非常简单的代码片段中发生了什么吗 (defun test-a () (let ((x '(nil))) (setcar x (cons 1 (car x))) x)) 在第一次调用(test-a)时,我得到了预期的结果:((1))。 但令我惊讶的是,再次调用它,我得到了((11)),((11))等等。 为什么会这样?我期望(test-a)总是返回((1)),这是错误的吗? 还要注意的是,在重新评估test-a的定义后,返回结果将重置 也考虑到这个函数和

有人能给我解释一下这个非常简单的代码片段中发生了什么吗

(defun test-a ()
  (let ((x '(nil)))
    (setcar x (cons 1 (car x)))
    x))
在第一次调用
(test-a)
时,我得到了预期的结果:
((1))
。 但令我惊讶的是,再次调用它,我得到了
((11))
((11))
等等。 为什么会这样?我期望
(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
不相等?

看起来(let)中的“(nil)只计算一次。当您(设置CAR)时,每个调用都在修改相同的列表。如果您将“(nil)替换为(list(list)),则可以使(test-a)工作,尽管我认为有一种更优雅的方法可以做到这一点

(test-b)每次都从cons单元格构造一个全新的列表,这就是它工作方式不同的原因。

坏消息
test-a
是自修改代码。这是极其危险的。当变量
x
let
表单末尾消失时,其初始值将保留在函数对象中,这就是您要修改的值。请记住,在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))
(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
中存储新的对象。这种差异与
C++
中的
x.setField(42)
x=newmyobject(42)
类似

底线 一般来说,最好将数据(如
”(1)
视为常量-不要修改它们:

quote
返回参数,不计算它<代码>(引用x)产生
x
警告
引号
不构造其返回值,只返回 Lisp读取器预先构造的值(请参见信息节点 )。这意味着
(a.b)
不是 与
(cons'a'b)
相同:前者不包含cons。引用应该 为永远不会被副作用修改的常数保留, 除非你喜欢自我修改代码。查看信息中的常见陷阱 节点以获取以下情况下意外结果的示例: 引用的对象被修改

如果需要,请使用
list
cons
copy list
而不是
quote
创建它

另外,这已经在上复制了


PPS。另请参见相同的常见Lisp问题。

我发现罪魁祸首确实是“引用”。下面是它的文档字符串:

返回参数,但不计算它

警告:'quote'不构造其返回值,只返回值 Lisp读取器预先构造的值

应为将 永远不要被副作用修改,除非你喜欢自我修改代码。

为了方便,我也重写了

(setq test-a 
      (lambda () ((lambda (x) (setcar x (cons 1 (car x))) x) (quote (nil)))))
然后用

(funcall test-a)

看看“测试-a”是如何变化的。

谢谢,你的回答确实提供了一个解决办法,但我仍然不明白为什么会发生这种情况。x inside’test-a被声明为本地的,实际上它在函数外部是不可见的,但是为什么函数在调用之间保留关于x的信息呢?我猜,这是因为emacs在解析let中的文本时构造了列表,然后每次调用函数时都引用相同的列表。这就像是在使用指向解析树的指针来引用x一样。@abo abo:emacs不保留关于x的信息,而是保留关于x的初始值的信息。看我的答案。但是为什么修改会持续?变量x,我认为它只是一个指向
'(nil)
的指针,不应该在let表单的末尾被删除吗?@Tyler:变量被删除了,但初始值没有。请参阅编辑。既然您已经解释过了,这就有道理了。我永远不会建立这种连接。在定义
test-a
之后调用
(符号函数'test-a)
,以及在调用它之后再次调用都很有启发性。@abo abo在test-a中,setcar用于直接修改x指向的列表。X指向函数定义中的一个常量,因此函数本身会发生更改。在测试b中,x最初指向的列表中的值用于构造要分配给x的新列表。初始值永远不会直接改变。所以你是对的,赛卡是关键的区别。