ruby,试图返回多维数组的子集

时间:2012-11-14 01:46:00

标签: ruby multidimensional-array

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

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

事实上:

[  [ [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数组api文档,从我正在阅读的代码中我的代码应该毫无意外地工作......但是......仍然......

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

感谢所有花时间回答的人

2 个答案:

答案 0 :(得分:4)

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

这就是许多对象上有clonedup方法的原因。如果您打算在使用之前修改某些内容,但又不想破坏原始内容,请复制并使用它。

一种简单的方法是避免使用delete之类的就地修饰符,而是使用像reject这样的修饰符:

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

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

space_subset[1] -= [ 8 ]

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

答案 1 :(得分:3)

这是参考之间的差异。在您的代码中,您创建了对内部数组的引用,但是您在两个地方都引用了相同的值。您可以通过在两个阵列上调用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#dupArray#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], []]

希望有所帮助!