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上測試它。
添加回答
舉報