Unexpected persistence of data [duplicate]

2019-01-02 16:19发布

This question already has an answer here:

I have a list of seven integers, initially all 0s, let's call it "data." Periodically during the course of running my program I want to increment the value of one of those integers by one. At the end of the program I print data. All is fine, except that on each successive run of the program all the values of data from the last run are added to all the values of data from this run. I want only the values of data from this run. This unexpected behavior occurs whether data is a local variable within a class's method, a local variable within a separate function called by a class's method, or a slot of a class. It happens whether I increment the individual values of data by incf or (setf value (1+ value)). When I reload the program, data resets to all zeroes but when I run the program again data again adds all of the last run's data to this run's data. When I increment one of the values of data I use the function nth with index being the value of another object's slot. What could cause this unwelcome persistence of values of my "data" list?

1条回答
步步皆殇っ
2楼-- · 2019-01-02 16:35

Are you doing something like this:

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

Quoted data is literal data; there's only one copy of it, and the consequences of modifying it are undefined. The behavior above is common, but you can't depend on it. Some compilers will give a warning when you do this. E.g., in 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

The relevant text from the HyperSpec on quote is:

The consequences are undefined if literal objects (including quoted objects) are destructively modified.

Create modifiable lists with, e.g., (list 1), not '(1). This is a common pitfall until you've encountered it. There are some other questions on StackOverflow that mention this issue. A very specific one is

but there are a bunch, too:

The same thing happens in Scheme, though the citations to the documentation are obviously different. For R5RS, the documentation is the following:

4.1.2 Literal expressions

… As noted in section 3.4, it is an error to alter a constant (i.e. the value of a literal expression) using a mutation procedure like set-car! or string-set!.

3.4 Storage model

… In many systems it is desirable for constants (i.e. the values of literal expressions) to reside in read-only-memory. To express this, it is convenient to imagine that every object that denotes locations is associated with a flag telling whether that object is mutable or immutable. In such systems literal constants and the strings returned by symbol->string are immutable objects, while all objects created by the other procedures listed in this report are mutable. It is an error to attempt to store a new value into a location that is denoted by an immutable object.

There are questions about this as well:

查看更多
登录 后发表回答