Unexpected persistence of data [duplicate]

2019-01-02 16:42发布

问题:

This question already has an answer here:

  • Why does this function return a different value every time? 4 answers

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:

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

  • Why does this function return a different value every time?

but there are a bunch, too:

  • LISP: Why doesn't mapcan accept my list give as parameters? (an interesting case with a literal list inside a lambda function passed to mapcan)
  • Unexpected List Duplication using Sort with Common Lisp
  • LISP - Global variable keep their old value after reinitialization
  • this answer to Sudoku table generator failure, lisp
  • How does Lisp function remember state in this code?
  • Do property lists in Common Lisp refer to some global state?
  • Why does this function return a different value every time?
  • Strange behavior invoking destructive Common LISP function receiving as argument a list created with quote
  • Local variable keeps data from previous execution
  • What is happening with this Common Lisp code?
  • Unexpected persistence of data (this question)
  • setf in a function does not work
  • duplicating and modifying the head of a list of list, in Lisp

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:

  • How could I explain this unexpected behavior of my Scheme code?
  • Difference between '(()) and (cons null null)


标签: