user3051121
user3051121

Reputation: 13

Copy of a 2d array instead of reference in CLISP

I am trying to create a copy of the first element in the array and add the copy to the end of the array. I then want to do work (move_NE) on the copy I just created, changing it but not the original. The intended result is to have an array of two elements, one which points to the original and the other which points to a modified original.

(vector-push-extend (initialize_board 5) *all_states*)
(vector-push-extend (elt *all_states* 0) *all_states*)
(move_NE (elt *all_states* 1) 0 2)

From what I can figure, (elt *all_states* 0) is producing a reference to the original element which results in an array with two elements, both which point to the same thing.

The context of this program is from my attempts to write a program to generate all possible moves for a triangular peg solitaire (cracker barrel) game. *all_states* is an array of boardstates, each of which are a 2d array.

Any help is appreciated.

EDIT: My background is in C/C++ programming.

Upvotes: 0

Views: 197

Answers (1)

Joshua Taylor
Joshua Taylor

Reputation: 85913

There's no copying-on-assignment in Common Lisp. (And, as far as I'm aware, There's not in most Object Oriented Programming languages, either. E.g., in Java, if you have Object x = ...; Object y = x; there's just one object. If you modify that object through either the variable x or y, the change will be visible if you access the object through the other variable.) If you need a copy of an object, you'll need to make that copy yourself. That's just the same for other built in datatypes, too.

First, note that if you store a value in an element of an array, it doesn't modify the previous value that was stored in that array:

CL-USER> (let ((a (make-array 10 :adjustable t :fill-pointer 1)))
           (setf (aref a 0) "one")
           (print a)
           (vector-push-extend (aref a 0) a)
           (print a)
           (setf (aref a 1) "five")
           (print a))

; #("one") 
; #("one" "one") 
; #("one" "five") 

But, when the array looked like #("one" "one"), the value of (aref a 0) and (aref a 1) is the same string. You can see this if we modify that string:

CL-USER> (let ((a (make-array 10 :adjustable t :fill-pointer 1)))
           (setf (aref a 0) "one")
           (print a)
           (vector-push-extend (aref a 0) a)
           (setf (char (aref a 1) 2) #\3)
           (print a))

; #("one") 
; #("on3" "on3") ; we changed the **single** string

When you extend the array you can, of course, make a copy of the object, and then there will be two distinct objects:

CL-USER> (let ((a (make-array 10 :adjustable t :fill-pointer 1)))
           (setf (aref a 0) "one")
           (print a)
           (vector-push-extend (copy-seq (aref a 0)) a)
           (print a)
           (setf (char (aref a 1) 2) #\3)
           (print a))

; #("one") 
; #("one" "one") 
; #("one" "on3")

You mentioned

From what I can figure, (elt *all_states* 0) is producing a reference to the original element which results in an array with two elements, both which point to the same thing.

That's really the behavior that you want. If (elt *all_states* 0) didn't return the object at index 0 of the array, but returned a copy of the object, there'd be no way to modify the actual thing that's stored in the array (if the array was the only way to get ahold of the object). You mentioned coming from a C/C++ background; I highly recommend that rather than try to adapt that mental model to become a mental model for Common Lisp, that you spend some time building a mental model of Common Lisp from (almost) scratch. I don't mean that in a dismissive sense; in my opinion, it's good advice for any programmer learning a new language. If you try to "get by" with assumptions based on other languages, you can end up with some pretty subtle and hard-to-find bugs. I'd make a similar suggestion to someone with a Lisp background learning C/C++. If, for some reason, you don't have the time to do that, the quickest and safest advice I can give you is this:

  • If you you need to think of Common Lisp with a C/C++ model, choose C, not C++. Primitive datatypes (ints, chars, etc.) are roughly the same, and everything is is handled by pointers.

With that model, then your initial problem is very clear. You've got an array of pointers to objects, and you extended an array with another pointer to the same object. It's no surprise, then, that when you modified the object pointed at by that pointer, it was visible through all pointers to that object. You need to allocate a new object that's a copy of the first, and put a pointer to that in the array. This is really the behavior that you want

Upvotes: 1

Related Questions