Strange behavior invoking destructive Common LISP function receiving as argument a list created with quote
Asked Answered
O

1

1

I've been getting a strange behavior when invoking a destructive definition receiving as argument a local variable whose type is a list created with a quote.

Destructive function:

(defun insert-at-pos (pos list elem)
  (if (= pos 0)
      (cons elem list)
      (let ((aux-list (nthcdr (1- pos) list)))
        (setf (rest aux-list) (cons elem (rest aux-list)))
        list)))

WRONG: Local variable is a list created with the special operator quote.

(defun test ()
 (let ((l '(1 2 3)))
   (print l)
   (insert-at-pos 2 l 4)
   (print l))) 

> (test)

(1 2 3)
(1 2 4 3)
(1 2 4 3)

> (test)

(1 2 4 3)
(1 2 4 4 3)
(1 2 4 4 3)

> (test)

(1 2 4 4 3)
(1 2 4 4 4 3)
(1 2 4 4 4 3) 

CORRECT: Local variable is a list created with function list.

(defun test2 ()
 (let ((l (list 1 2 3)))
   (print l)
   (insert-at-pos 2 l 4)
   (print l)))

or

(defun test2 ()
 (let ((l '(1 2 3)))
   (print l)
   (setf l (cons (first l) (cons (second l) (cons 4 (nthcdr 2 l)))))
   (print l)))

> (test2)

(1 2 3)
(1 2 4 3)
(1 2 4 3)

> (test2)

(1 2 3)
(1 2 4 3)
(1 2 4 3)

> (test2)

(1 2 3)
(1 2 4 3)
(1 2 4 3)

Does someone know the reason of this strange behaviour?

Olnee answered 3/6, 2013 at 8:53 Comment(2)
in your second test2 you setf a variable l, which is a perfectly OK thing to do. In your original insert-at-pos definition you setf a place, a part of a cons node structure, which is a no-no. (setf (rest aux-list) ... translates into RPLACD call.Ranitta
possible duplicate of Unexpected persistence of dataArborvitae
E
8

If you quote data in a function, then it is literal data. The effects of destructively modifying such literal data are undefined in the Common Lisp standard. In your example all function invocations share the same literal data and the implementation does not warn you that you are changing it. That's what most implementations do. But it would also possible to imagine an implementation which puts all code (and its literal data) into a read-only part of the memory.

You can get funky effects with this.

If you want to destructively modify a list without running into potential problems, then you need to create a fresh copy at runtime. For example by calling LIST or COPY-LIST. LIST will return a fresh consed list.

There are similar pitfalls. For example imagine a file with these definitions:

(defvar *foo* '(1 2 3 4 5 6 ... 10000))

(defvar *foo* '(0 1 2 3 4 5 6 ... 10000))

If you compile such a file with the file compiler, the compiler is allowed to create a compiled file, where the two variables share literal data - saving space. If you would change an element in either list, both might be changed.

Eckblad answered 3/6, 2013 at 9:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.