3 回答
TA贡献2051条经验 获得超10个赞
你在做这样的事情:
CL-USER> (defun foo ()
(let ((value '(1))) ; '(1) is literal data
(incf (car value))))
FOO
CL-USER> (foo)
2
CL-USER> (foo)
3
CL-USER> (foo)
4
CL-USER> (foo)
5
引用的数据是文字数据; 它只有一个副本,修改它的后果是不确定的。上面的行为很常见,但你不能依赖它。有些编译器会在您执行此操作时发出警告。例如,在SBCL中:
CL-USER> (defun foo ()
(let ((value '(1)))
(incf (car value))))
; in: DEFUN FOO
; (INCF (CAR VALUE))
; --> LET*
; ==>
; (SB-KERNEL:%RPLACA #:TMP1 #:NEW0)
;
; caught WARNING:
; Destructive function SB-KERNEL:%RPLACA called on constant data.
; See also:
; The ANSI Standard, Special Operator QUOTE
; The ANSI Standard, Section 3.2.2.3
;
; compilation unit finished
; caught 1 WARNING condition
FOO
HyperSpec的相关文本quote是:
如果破坏性地修改文字对象(包括引用对象),则后果是不确定的。
创建可修改的列表,例如(list 1),不'(1)。在你遇到它之前,这是一个常见的陷阱。
在Scheme中也会发生同样的事情,尽管对文档的引用明显不同。对于R 5 RS,文档如下:
4.1.2文字表达
......如3.4节所述,使用像set-car这样的变异程序来改变常量(即文字表达式的值)是错误的!或字符串集!
3.4存储模型
...在许多系统中,希望常量(即文字表达式的值)驻留在只读存储器中。为了表达这一点,可以很方便地设想表示位置的每个对象都与一个标志相关联,该标志告诉该对象是可变的还是不可变的。在这样的系统中,文字常量和symbol-> string返回的字符串是不可变对象,而本报告中列出的其他过程创建的所有对象都是可变的。尝试将新值存储到由不可变对象表示的位置是错误的。
TA贡献1757条经验 获得超8个赞
不要求发出警告。行为仍未定义。即使在某些情况下发出警告的实现中,我也看不到任何保证他们在所有情况下都这样做。你的代码是在你编译的文件中吗?或者您在REPL中输入的内容?您可以写信给SBCL邮件列表并询问此具体案例。
TA贡献1982条经验 获得超2个赞
另一个问题,如果我在SBCL中定义一个函数:(defun set-head (x v) (rplaca x v))
,那为什么这个表达式(let ((x '(a b))) (set-head x 'z))
不会引发警告?我在SBCL 1.2.7上测试它。
添加回答
举报