arrays - 二维数组的副本而不是 CLISP 中的引用

标签 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) 正在生成对原始元素的引用,这导致一个包含两个元素的数组,这两个元素都指向同一事物。

这个程序的上下文来自于我尝试编写一个程序来为三角钉纸牌( cookies 桶)游戏生成所有可能的移动。 *all_states* 是一个 boardstates 数组,每个 boardstates 都是一个二维数组。

感谢任何帮助。

编辑:我的背景是 C/C++ 编程。

最佳答案

Common Lisp 中没有赋值时复制。 (而且,据我所知,大多数面向对象编程语言中也没有。例如,在 Java 中,如果您有 Object x = ...; Object y = x;只有一个对象。如果您通过变量 xy 修改该对象,如果您通过另一个变量访问该对象,则更改将可见。)如果您需要对象的副本,您需要自己制作该副本。其他内置数据类型也是如此。

首先,请注意,如果您存储一个数组元素中的值,它不会修改存储在该数组中的先前值:

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")

你提到

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.

这确实是您想要的行为。如果 (elt *all_states* 0) 没有返回数组索引 0 处的对象,而是返回了该对象的一个​​副本,就没有办法修改存储在数组中的实际内容(如果数组是获取对象的唯一方法)。您提到来自 C/C++ 背景;我强烈建议您不要尝试将心智模型调整为 Common Lisp 的心智模型,而是花一些时间(几乎)从头开始构建 Common Lisp 的心智模型。我的意思不是轻蔑。在我看来,这对任何学习一门新语言的程序员来说都是很好的建议。如果您尝试“通过”基于其他语言的假设,您最终可能会遇到一些非常微妙且难以发现的错误。我会向具有 Lisp 背景的学习 C/C++ 的人提出类似的建议。如果出于某种原因,您没有时间这样做,我可以给您的最快捷、最安全的建议是:

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

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

关于arrays - 二维数组的副本而不是 CLISP 中的引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20295932/

相关文章:

php - 从 php 中的数据数值数组创建图像

lisp - 将 Lisp 代码形成任务——与展平列表方法相关

functional-programming - Lisp 中的顺序过程

clojure - 我们应该如何称呼在 clojure prog 中声明的对象?

javascript - MVC : Iterating a Viewbag array in javascript

javascript - 如何在不创建新数组的情况下用另一个数组扩展现有 JavaScript 数组

c++ - 2 维数组的大小

oop - Scheme 可以与 Microsoft COM 一起使用吗?

scala - 类型构造函数是单子(monad)还是有单子(monad)?

scala - 递归函数不返回 Int