我正在尝试创建数组中第一个元素的副本,并将副本添加到数组的末尾。然后我想在我刚刚创建的副本上做工作(move_NE),改变它而不是原始的。预期的结果是有一个包含两个元素的数组,一个指向原始元素,另一个指向修改后的原始元素。
(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)
从我的图中可以看出,(elt * all_states * 0)正在产生对原始元素的引用,这会导致一个包含两个元素的数组,这两个元素都指向同一个元素。
这个程序的背景来自于我尝试编写一个程序,以生成三角钉接头(饼干桶)游戏的所有可能的移动。 * all_states *是一个董事会状态数组,每个都是一个二维数组。
感谢任何帮助。
编辑:我的背景是C / C ++编程。
答案 0 :(得分:1)
Common Lisp中没有复制任务。 (而且,就我所知,大多数面向对象编程语言都没有。例如,在Java中,如果你有Object x = ...; Object y = x;
,那么只有一个对象。如果通过变量修改该对象{ {1}}或x
,如果您通过另一个变量访问对象,则可以看到更改。)如果您需要对象的副本,则需要自己制作该副本。对于其他内置数据类型也是如此。
首先,请注意,如果存储数组元素中的值,则不会修改存储在该数组中的先前值:
y
但是,当数组看起来像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")
时,#("one" "one")
和(aref a 0)
的值是相同的字符串。如果我们修改该字符串,您可以看到这一点:
(aref a 1)
当您扩展数组时,您当然可以复制该对象,然后会有两个不同的对象:
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
你提到了
从我的意图来看,
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")
正在产生一个参考 到原始元素导致数组有两个元素, 两者都指向同一件事。
这真的是你想要的行为。如果(elt *all_states* 0)
没有返回数组的索引(elt *all_states* 0)
处的对象,但返回了对象的副本,则无法修改存储在数组中的实际内容(如果数组是获取对象的唯一方法。你提到过来自C / C ++背景;我强烈建议你不要试图让那个心智模型成为Common Lisp的心智模型,而是花一些时间从(几乎)开始构建Common Lisp的心理模型。我并不是说在不屑一顾的意义上;在我看来,对于任何学习新语言的程序员来说,这都是一个很好的建议。如果你尝试使用基于其他语言的假设“顺便”,你可能会得到一些非常微妙且难以发现的错误。我会向一个学习C / C ++的Lisp背景的人提出类似的建议。如果,由于某种原因,你没有时间这样做,我可以给你的最快最安全的建议是:
使用该模型,您的初始问题非常明确。你有一个指向对象的数组,你用一个指向同一个对象的指针扩展了一个数组。因此,当您修改该指针指向的对象时,通过指向该对象的所有指针都可以看到它,这并不奇怪。您需要分配一个新对象,该对象是第一个的副本,并在数组中放置一个指针。 这实际上是你想要的行为