什么';(列表为零)和';(无)在Lisp中?
首先,让我说我是Lisp的初学者。老实说,我已经是初学者一段时间了,但还有很多事情我不太清楚 在我写作的时候,我在代码中发现了一个奇怪的bug 下面是一个函数,它将返回列表什么';(列表为零)和';(无)在Lisp中?,lisp,common-lisp,literals,Lisp,Common Lisp,Literals,首先,让我说我是Lisp的初学者。老实说,我已经是初学者一段时间了,但还有很多事情我不太清楚 在我写作的时候,我在代码中发现了一个奇怪的bug 下面是一个函数,它将返回列表(0 1…n),并附加列表e。它使用rplacd跟踪最后一个元素,以避免最后调用last 例如,(foo4'(x))返回(01234x) “head”存储在a中,它不是简单的nil,因为只有一个nil,而且从来没有它的副本(如果我理解正确),因此我不能简单地附加到nil (defun foo (n e) (let* (
(0 1…n)
,并附加列表e
。它使用rplacd
跟踪最后一个元素,以避免最后调用last
例如,(foo4'(x))
返回(01234x)
“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)))))
这些函数之间的唯一区别是栏中的(列表无)
被(无)
替换。当foo
按预期工作时,bar
始终返回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)))))
我最初猜测这是因为a
的原始cdr
确实是nil
,并且引用的列表可能被认为是常量。然而,如果我做了(setfx'(nil))(rplacdx1)
我得到了预期的(nil.1)
,所以我至少部分错了。当计算时,“(nil)和(list nil)产生了类似的列表,但前者在源代码中出现时可以被认为是常量。您不应该对Common Lisp中的常量引用列表执行任何破坏性操作。见和。特别是,后者说“如果文字对象(包括引用的对象)被破坏性地修改,后果是不确定的。”引用的数据被认为是一个常量。如果您有两个功能:
(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
相同的列表(读者可能会这样做)
实施过程中可以看到它是相同的,hench节省了空间:
(test2) ; ==> (0)
(test) ; ==> 1
(test) ; ==> 2
(test) ; ==> 3
(test2) ; ==> (3)
事实上。后两种行为可能发生在同一个实现中,具体取决于正在编译的函数与否
在CLISP中,两个函数的工作原理相同。在使用SBCL进行反汇编时,我还看到常量实际上发生了变异,因此我想知道它是否在编译时折叠了常量(cdr'(0))
,并且根本不使用变异列表。这其实并不重要,因为两者都被认为是良好的“未定义”行为
关于这一点的部分很短
如果文字对象(包括带引号的
对象)进行破坏性修改
看起来条的括号不平衡。@user2357112已更正。缺少的结束参数正好在'(nil)
之后。很抱歉,我不熟悉Common Lisp,但我相信Scheme,任何引用了“
的内容都应该被视为不可变的。这在公共口齿不清中适用吗?看起来你在试图改变这个东西。@user2357112是的,我从我过去使用Scheme的经验中记得这一点。在Common Lisp中似乎不一样。但我很谨慎,因为我怀疑我的bug与此有关。我的另一个猜测是,在一个版本中,您将得到一个带有符号nil
的列表,而在另一个版本中,nil
将计算为空列表。编辑:不,看起来符号nil
的计算结果是符号nil
。这很有意义,谢谢。所以,可以肯定的是,如果(setf x'(nil))(rplacd x 1)
做了我错误地期望的事情,这只会归功于未定义的后果?这里的方案更友好,因为它会通知错误IIRW。当SBCL能够检测到常量数据的修改时,它将发出有用的警告并引用标准的适用位。(我就是这样查找我的回复的参考资料的。)但它还不能检测到所有情况。在那之前,你只需要知道规则。@Jean-ClaudeArbaut:一个旧的常见问题解答说:R5RS的第3.4节指出,试图修改不可变对象(如文字列表)是错误的。不过,并不是所有方案都将此报告为错误,而是修改文字列表。谢谢。可以肯定的是,如果我复制一个带引号的列表,副本是可变的,对吗?@Jean-ClaudeArbaut是的,但只有顶层,因为副本列表
是浅副本。因此,它不会像以前那样递归地复制列表中的列表元素。还有一个问题:在for ncoc函数中,有一个例子:(setq x'(abc))(setq y'(def))(ncoc x y)这是不是错了?我的意思是,x和y都有一个临时列表,x由NCOC修改。