Lisp 中的 (list nil) 和 '(nil) 有什么区别?
What's the difference between (list nil) and '(nil) in Lisp?
首先,让我说我是 Lisp 的初学者。老实说我已经是新手有一段时间了,但是还有很多东西我不太了解。
当我在写 的时候,我在我的代码中发现了一个奇怪的错误。
这是一个函数,它将 return 列表 (0 1 ... n)
附加到列表 e
。它在整个过程中使用 rplacd
来跟踪最后一个元素,以避免最终调用 last
.
例如,(foo 4 '(x))
returns (0 1 2 3 4 x)
.
"head" 存储在 a
中,这不是简单的 nil
,因为只有一个 nil
,并且永远不会复制它(如果我理解正确),因此我不能简单地附加到 nil
.
(defun foo (n e)
(let* ((a (list nil)) (tail a))
(loop for i to n
do (rplacd tail (setf tail (list i)))
finally (rplacd tail (setf tail e))
(return (cdr a)))))
(defun bar (n e)
(let* ((a '(nil)) (tail a))
(loop for i to n
do (rplacd tail (setf tail (list i)))
finally (rplacd tail (setf tail e))
(return (cdr a)))))
这些函数之间的唯一区别是 (list nil)
替换为 bar
中的 '(nil)
。虽然 foo
按预期工作,但 bar
总是 returns nil
.
我最初的猜测是因为 a
的原始 cdr
确实是 nil
,并且引用列表可能被认为是常量。但是,如果我这样做 (setf x '(nil)) (rplacd x 1)
,我会按预期得到 (nil . 1)
,所以我一定至少有部分错误。
计算时,'(nil) 和 (list nil) 生成相似的列表,但前者在源代码中出现时可以被视为常量。你不应该对 Common Lisp 中的常量引用列表执行任何破坏性操作。参见 http://l1sp.org/cl/3.2.2.3 and http://l1sp.org/cl/quote。特别是,后者说 "The consequences are undefined if literal objects (including quoted objects) are destructively modified."
引用的数据被认为是常量。如果你有两个函数:
(defun test (&optional (arg '(0)))
(setf (car arg) (1+ (car arg)))
(car arg))
(defun test2 ()
'(0))
这两个函数都使用常量列表 (0)
对吧?
实现可以选择不改变常量:
(test) ; ==> Error, into the debugger we go
实现可以 cons
相同的列表两次(reader 可能会这样做)
(test2) ; ==> (0)
(test) ; ==> 1
(test) ; ==> 2
(test) ; ==> 3
(test2) ; ==> (0)
实现可以看到和hench save是一样的space:
(test2) ; ==> (0)
(test) ; ==> 1
(test) ; ==> 2
(test) ; ==> 3
(test2) ; ==> (3)
事实上。最后两个行为可能会在同一实现中发生,具体取决于正在编译的函数。
在 CLISP 中,这两个函数的工作原理相同。我还看到在使用 SBCL 反汇编时常量实际上发生了变异,所以我想知道它是否在编译时常量折叠 (cdr '(0))
并且根本不使用变异列表。这真的无关紧要,因为两者都被认为是良好的 "undefined" 行为。
这部分 from CLHS 很短
The consequences are undefined if literal objects (including quoted
objects) are destructively modified.
首先,让我说我是 Lisp 的初学者。老实说我已经是新手有一段时间了,但是还有很多东西我不太了解。
当我在写
这是一个函数,它将 return 列表 (0 1 ... n)
附加到列表 e
。它在整个过程中使用 rplacd
来跟踪最后一个元素,以避免最终调用 last
.
例如,(foo 4 '(x))
returns (0 1 2 3 4 x)
.
"head" 存储在 a
中,这不是简单的 nil
,因为只有一个 nil
,并且永远不会复制它(如果我理解正确),因此我不能简单地附加到 nil
.
(defun foo (n e)
(let* ((a (list nil)) (tail a))
(loop for i to n
do (rplacd tail (setf tail (list i)))
finally (rplacd tail (setf tail e))
(return (cdr a)))))
(defun bar (n e)
(let* ((a '(nil)) (tail a))
(loop for i to n
do (rplacd tail (setf tail (list i)))
finally (rplacd tail (setf tail e))
(return (cdr a)))))
这些函数之间的唯一区别是 (list nil)
替换为 bar
中的 '(nil)
。虽然 foo
按预期工作,但 bar
总是 returns nil
.
我最初的猜测是因为 a
的原始 cdr
确实是 nil
,并且引用列表可能被认为是常量。但是,如果我这样做 (setf x '(nil)) (rplacd x 1)
,我会按预期得到 (nil . 1)
,所以我一定至少有部分错误。
计算时,'(nil) 和 (list nil) 生成相似的列表,但前者在源代码中出现时可以被视为常量。你不应该对 Common Lisp 中的常量引用列表执行任何破坏性操作。参见 http://l1sp.org/cl/3.2.2.3 and http://l1sp.org/cl/quote。特别是,后者说 "The consequences are undefined if literal objects (including quoted objects) are destructively modified."
引用的数据被认为是常量。如果你有两个函数:
(defun test (&optional (arg '(0)))
(setf (car arg) (1+ (car arg)))
(car arg))
(defun test2 ()
'(0))
这两个函数都使用常量列表 (0)
对吧?
实现可以选择不改变常量:
(test) ; ==> Error, into the debugger we go
实现可以
cons
相同的列表两次(reader 可能会这样做)(test2) ; ==> (0) (test) ; ==> 1 (test) ; ==> 2 (test) ; ==> 3 (test2) ; ==> (0)
实现可以看到和hench save是一样的space:
(test2) ; ==> (0) (test) ; ==> 1 (test) ; ==> 2 (test) ; ==> 3 (test2) ; ==> (3)
事实上。最后两个行为可能会在同一实现中发生,具体取决于正在编译的函数。
在 CLISP 中,这两个函数的工作原理相同。我还看到在使用 SBCL 反汇编时常量实际上发生了变异,所以我想知道它是否在编译时常量折叠 (cdr '(0))
并且根本不使用变异列表。这真的无关紧要,因为两者都被认为是良好的 "undefined" 行为。
这部分 from CLHS 很短
The consequences are undefined if literal objects (including quoted objects) are destructively modified.