[comp.lang.lisp] Problems Copying Structures

flan@cics.wustl.EDU (Ian Flanigan) (12/08/90)

I've run into a big, big snag using AKCL 1.492.  I think it's a general
problem with Common Lisp, but here goes:

In my program I define a structure with defstruct:

(defstruct hexboard
  (board (make-array (list x-board-dimension y-board-dimension)
					 :element-type 'character
					 :initial-element #\Space))
  (ownership-list (make-array number-of-playersi
							  :element-type 'list
							  :initial-element NIL))
  (connection-list (make-array (list x-board-dimension y-board-dimension)
							   :element-type 'list
							   :initial-element NIL))
  )

Then I create an instance of the structure with:

> (setq x (make-hexboard))

Now I try to make a copy of that structure:

> (setq y (copy-hexboard x))

And change a few things:

> (setf (aref (hexboard-board y) 0 0) #\X)

Now I try to see what y is:

> y

#S(HEXBOARD BOARD
      #2A((#\X #\Space #\Space #\Space #\Space #\Space)
          (#\Space #\Space #\Space #\Space #\Space #\Space)
          (#\Space #\Space #\Space #\Space #\Space #\Space)
          (#\Space #\Space #\Space #\Space #\Space #\Space)
          (#\Space #\Space #\Space #\Space #\Space #\Space)
          (#\Space #\Space #\Space #\Space #\Space #\Space))
      OWNERSHIP-LIST #(NIL NIL) CONNECTION-LIST
      #2A((NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL)
          (NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL)
          (NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL)))

Now I see what x is:

> x

#S(HEXBOARD BOARD
      #2A((#\X #\Space #\Space #\Space #\Space #\Space)
          (#\Space #\Space #\Space #\Space #\Space #\Space)
          (#\Space #\Space #\Space #\Space #\Space #\Space)
          (#\Space #\Space #\Space #\Space #\Space #\Space)
          (#\Space #\Space #\Space #\Space #\Space #\Space)
          (#\Space #\Space #\Space #\Space #\Space #\Space))
      OWNERSHIP-LIST #(NIL NIL) CONNECTION-LIST
      #2A((NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL)
          (NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL)
          (NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL)))

How can I get it so that there are no side affects?  I've tried copy-tree,
but that doesn't work.  I can't think of anything else to do.

Any help would be greatly appreciated.

Thanks.

-- 
Ian Flanigan

flan@cics.wustl.edu              "You can never have too many napkins."
wucs1.wustl.edu!cics!flan@uucp

barmar@think.com (Barry Margolin) (12/09/90)

Ian Flanigan complained that when he copies a structure whose slots contain
arrays, and then modifies one of the arrays, the array in the other
structure is also modified.

Structure copiers are only supposed to copy the structure itself, not the
objects referenced by the structure.  They are like COPY-LIST rather than
COPY-TREE.  If you want more, you have to write your own copier function
that does precisely what you want.  There's no standard function that's
able to read the programmer's mind and determine which parts of a structure
should be copied and which should just be referenced.

Such a function is not too hard to write.  Given something like:

(defstruct my-structure
  (slot1 (make-array ...))
  (slot2 (make-array ...))
  (slot3 (make-array ...)))

(defun copy-my-structure-and-contents (my-s)
  (let ((old1 (my-structure-slot1 my-s))
	(old2 (my-structure-slot2 my-s))
	(old3 (my-structure-slot3 my-s)))
    (make-my-structure
      :slot1 (make-array (array-dimensions old1)
			 :element-type (array-element-type old1)
			 :initial-elements old1)
      :slot2 (make-array (array-dimensions old2)
			 :element-type (array-element-type old2)
			 :initial-elements old2)
      :slot3 (make-array (array-dimensions old3)
			 :element-type (array-element-type old3)
			 :initial-elements old3))))

Note that this still only copies one level more than the
automatically-generated copier.  If you also want to copy the objects
referenced in the arrays you'll have to do more.
--
Barry Margolin, Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar

srt@aerospace.aero.org (Scott "TCB" Turner) (12/11/90)

Barry writes:
>Note that this [structure copier] still only copies one level more
>than the automatically-generated copier.  If you also want to copy
>the objects referenced in the arrays you'll have to do more.

One of the better arguments for object-oriented programming.

					-- Scott Turner

jgk@osc.COM (Joe Keane) (12/14/90)

In article <1990Dec8.180825.21702@Think.COM> barmar@think.com (Barry Margolin)
writes:
>Structure copiers are only supposed to copy the structure itself, not the
>objects referenced by the structure.  They are like COPY-LIST rather than
>COPY-TREE.  If you want more, you have to write your own copier function
>that does precisely what you want.  There's no standard function that's
>able to read the programmer's mind and determine which parts of a structure
>should be copied and which should just be referenced.

>Note that this [structure copier] still only copies one level more
>than the automatically-generated copier.  If you also want to copy
>the objects referenced in the arrays you'll have to do more.

In article <93985@aerospace.AERO.ORG> srt@aerospace.aero.org (Scott "TCB"
Turner) writes:
>One of the better arguments for object-oriented programming.

I don't see what object-oriented programming has to do with this.  The same
problems with deep vs. shallow copying come up, and there's no magic solution
which always does what you want.  One thing i do know is that it's a hell of a
lot easier to automatically generate functions you want in Lisp than in C++.

srt@aerospace.aero.org (Scott "TCB" Turner) (12/15/90)

Joe Keane writes:
>In article <93985@aerospace.AERO.ORG> srt@aerospace.aero.org (Scott "TCB"
>Turner) writes (about structure copying):
>>One of the better arguments for object-oriented programming.
>
>I don't see what object-oriented programming has to do with this.  The same
>problems with deep vs. shallow copying come up, and there's no magic solution
>which always does what you want.

Imagine trying to write a "copy" function in generic Lisp that will take
any type of object and provide an unlimited depth copy of that object.  
You do something along these lines:

	(defun copy (foo)
	   (cond ((type-1 foo)
			(apply #'create-new-type-1-w-elements
			       (mapc #'copy (destructure-type-1 foo))))
		 ((type-2 foo) ...)))

For each type that you know about, you destructure it, copy all the 
elements and then put it back together into a new gestalt.

Problem is, this requires that you know how to destructure and create
each possible type that will ever exist in your system.  Difficult.

In object-oriented programming, each type simply has an operation
"copy" that knows how to destructure and create that type and "voila!"
the problem is solved.

The same information and problems exist in either case.  OOP is not a
panacea, no matter what certain C++ cultists seem to think.  But OOP
does make this problem more tractable (and expandable) but organizing
the information wisely.

					-- Scott Turner