2

我试图返回一个多维数组的子集,试图保持维度的确切结构,但是..发生了一些奇怪的事情......请看一下:

space = [  [ [1],[2],[3] ],  [ [4],[5],[6] ],  [ [70],[8],[9] ]  ]

space_subset = space[(1..2)].collect { |y| y[1] }

=> [[5], [8]] 

让我们分解一下:

space[(1..2)]

=> [  [ [4], [5], [6] ], [ [70], [8], [9] ]  ]

所以现在我可以确定我在调用什么 .collect on

实际上:

[  [ [4], [5], [6] ], [ [70], [8], [9] ]  ].collect { |y| y[1] }

=> [[5], [8]]

然后......(对于真正的问题)......

如果现在 space_subset 是 [[5], [8]]

我尝试像这样修改它:

space_subset[1].delete (8)

正如预期的那样,我得到:=> [[5], []]

为什么这同时修改了我从中提取子集数组的原始“空间”数组?

如果现在我这样做:

space

=> [[[1], [2], [3]], [[4], [5], [6]], [[70], [], [9]]]

缺少“8”,与我从 space_subset 中删除的值相同

我正在查看 ruby​​ Array api 文档,从我正在阅读的内容来看,我的代码应该可以毫无意外地工作......但是......仍然......

你能帮我弄清楚我做错了什么,或者这里有什么误解吗?

感谢所有花时间回答的人

4

2 回答 2

4

请记住,在 Ruby 中,不仅一切都是对象,而且变量始终是对对象的引用。当您得到的是对原始单元素数组的引用时,您期望在此处制作副本。

这就是为什么在许多对象上有clone或方法。dup如果您打算在使用之前对其进行修改,但又不想破坏原件,请制作一个副本并使用它。

一种简单的方法是避免使用就地修饰符 likedelete而是使用一个 like reject

space_subset[1] = space_subset[1].reject { |v| v == 8 }

这将删除单个元素并返回原始数组的副本减去该元素。不过,这不一定是最好的方法。更好的方法可能是简单地“减去”您不想要的元素,因为这也会返回一个副本:

space_subset[1] -= [ 8 ]

一般来说,您必须警惕对您不“拥有”的数据使用就地修饰符。为了安全起见,您应该使用生成修改副本的操作。

于 2012-11-14T02:11:15.883 回答
3

这是referencevalue之间的差异。在您的代码中,您创建了对内部数组的引用,但您在两个地方都引用了相同的值。您可以通过调用两个数组来确认这一点Object#object_id(好像通过一个引用更改值并看到从另一个引用进行修改是不够的确认!)。

space = [  [ [1],[2],[3] ],  [ [4],[5],[6] ],  [ [70],[8],[9] ]  ]
=> [[[1], [2], [3]], [[4], [5], [6]], [[70], [8], [9]]] 
space[2][1].object_id
=> 70329700053380 
space_subset = space[(1..2)].collect { |y| y[1] }
=> [[5], [8]] 
space_subset[1].object_id
=> 70329700053380

不幸的是,Array#dup并且Array#clone只制作对象的“浅”副本,因此您必须使用一些变通方法来获取要使用的副本space。获得深层副本的一个简单技巧是:

Marshal.load(Marshal.dump(space))

您还可以编写一个递归函数来获取space并手动将其复制到一个新数组中。

只是为了证明这一点:

space = [  [ [1],[2],[3] ],  [ [4],[5],[6] ],  [ [70],[8],[9] ]  ]
=> [[[1], [2], [3]], [[4], [5], [6]], [[70], [8], [9]]]
space[2][1].object_id
=> 70329700053380
space_subset = Marshal.load(Marshal.dump(space))
=> [[[1], [2], [3]], [[4], [5], [6]], [[70], [8], [9]]]
space_subset = space_subset[(1..2)].collect { |y| y[1] }
=> [[5], [8]]
space_subset[1].object_id
=> 70329695297500
space_subset[1].delete(8)
=> 8
space
=> [[[1], [2], [3]], [[4], [5], [6]], [[70], [8], [9]]]
space_subset
=> [[5], []]

希望有帮助!

于 2012-11-14T02:24:51.413 回答