【问题标题】:Copy of a 2d array instead of reference in CLISP2d 数组的副本,而不是 CLISP 中的引用
【发布时间】:2013-12-16 06:01:01
【问题描述】:

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

【问题讨论】:

  • (elt *all_states* n) 返回索引n 处的数组元素。如果您想在其他地方存储副本,则需要自己制作副本,然后将其放入数组中。您为复制状态而编写的函数的名称是什么?
  • 我的方法应该是(vector-push-extend (make_a_copy (elt *all_states* 0)) *all_states*)吗?来自 oo 背景,如果我说 v[0] = 1; v.push_back(v[0]); v[1] = 5;我期望向量 {1, 5}。应用到我目前的情况,我得到一个向量 {5, 5}。

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


【解决方案1】:

Common Lisp 中没有分配时复制。 (而且,据我所知,大多数面向对象编程语言中也没有。例如,在 Java 中,如果您有 Object x = ...; Object y = x;,则只有一个对象。如果您通过变量 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) 没有返回数组的索引0 处的对象,而是返回了对象的副本,则无法修改存储在数组中的实际内容(如果数组是唯一的获取对象的方法)。您提到来自 C/C++ 背景;我强烈建议您花一些时间从(几乎)零开始构建 Common Lisp 的心智模型,而不是尝试将该心智模型调整为 Common Lisp 的心智模型。我的意思不是不屑一顾。在我看来,对于任何学习一门新语言的程序员来说,这都是一个很好的建议。如果您尝试“通过”基于其他语言的假设,您最终可能会遇到一些非常微妙且难以发现的错误。我会向具有 Lisp 背景学习 C/C++ 的人提出类似的建议。如果由于某种原因您没有时间这样做,我能给您的最快和最安全的建议是:

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

使用该模型,您最初的问题就很清楚了。你有一个指向对象的指针数组,并且你用另一个指向同一个对象的指针扩展了一个数组。因此,当您修改该指针指向的对象时,通过指向该对象的所有指针都可以看到它,这并不奇怪。您需要分配一个作为第一个副本的新对象,并将指向该对象的指针放入数组中。 这确实是您想要的行为

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-20
    • 1970-01-01
    • 2019-11-16
    • 2012-11-25
    • 1970-01-01
    • 2015-07-23
    • 2013-07-06
    相关资源
    最近更新 更多