在LISP中复制2d数组而不是引用

时间:2013-11-30 02:48:32

标签: arrays functional-programming lisp common-lisp clisp

我正在尝试创建数组中第一个元素的副本,并将副本添加到数组的末尾。然后我想在我刚刚创建的副本上做工作(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 ++编程。

1 个答案:

答案 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背景的人提出类似的建议。如果,由于某种原因,你没有时间这样做,我可以给你的最快最安全的建议是:

  • 如果您需要考虑使用C / C ++模型的Common Lisp,请选择C,而不是C ++。原始数据类型(整数,字符等)大致相同,一切都由指针处理。

使用该模型,您的初始问题非常明确。你有一个指向对象的数组,你用一个指向同一个对象的指针扩展了一个数组。因此,当您修改该指针指向的对象时,通过指向该对象的所有指针都可以看到它,这并不奇怪。您需要分配一个新对象,该对象是第一个的副本,并在数组中放置一个指针。 这实际上是你想要的行为