Ruby 如何使用数组而不更改其初始值?

Ruby 如何使用数组而不更改其初始值?,ruby,dictionary,Ruby,Dictionary,我是个新手。到目前为止,我接触过两种语言:ruby和python。我还读了几篇关于C++指针的文章,我对数组对象有点困惑,以及如何操作内部的值。我发现在方法中包含感叹号可以在不赋值的情况下更改数组。然而,有时情况并非如此;如果有人能解释数组的性质,它们是什么,它们是存储值的位置,还是值的集合,我将不胜感激?更具体地说,如果我将ruby数组函数分为两组:一组可以直接更改数组的值而无需赋值,另一组需要赋值,您能给我举几个例子吗 在下面的示例中,我不理解为什么更改初始数组的值 下面是我的代码示例: e

我是个新手。到目前为止,我接触过两种语言:ruby和python。我还读了几篇关于C++指针的文章,我对数组对象有点困惑,以及如何操作内部的值。我发现在方法中包含感叹号可以在不赋值的情况下更改数组。然而,有时情况并非如此;如果有人能解释数组的性质,它们是什么,它们是存储值的位置,还是值的集合,我将不胜感激?更具体地说,如果我将ruby数组函数分为两组:一组可以直接更改数组的值而无需赋值,另一组需要赋值,您能给我举几个例子吗
在下面的示例中,我不理解为什么更改初始数组的值
下面是我的代码示例:

edges = [[[1, 2], [100, 200]], [[700, 400], [1000, 2000]]]

new_edges = Array.new(edges)
new_edges.map{|edge| edge.map{|point| point.insert(0, 808912)}}

edges
#=> [[[808912, 1, 2], [808912, 100, 200]], [[808912, 700, 400], [808912, 1000, 2000]]]

new_edges
#=> [[[808912, 1, 2], [808912, 100, 200]], [[808912, 700, 400], [808912, 1000, 2000]]]
我相信会发生的是,
中的值不会改变,新边也会改变。非常感谢您的帮助。

您从

edges = [[[1, 2], [100, 200]], [[700, 400], [1000, 2000]]]
Dup在这里不起作用,因为它是一个嵌套数组,所以在做你想做的事情之前,你需要对它进行深入的Dup。(如果不想改变原始数组)

下面是一个蒙基补丁的deep_dup(数组)示例

def deep_dup
新的_arr=[]
每个人都有自己的想法|
如果ele.is_a?排列

new_arr您的代码与Python中的问题相同:

edges = [[[1, 2], [100, 200]], [[700, 400], [1000, 2000]]]
new_edges = list(edges)
for edge in edges:
    for point in edge:
        point.insert(0, 808912)
print(edges)
也就是说,您转换外部数组,但内部数组仅通过引用保存,因此更改一个数组(通过
插入
)会更改
新边
的深层内容。这些类型的问题很容易理解,只需使用(尽管名称不同,但它适用于Python和Ruby)

在Ruby中,您可以使用
+
,而不是使用
insert
,它会修改数组,但不会:

edges = [[[1, 2], [100, 200]], [[700, 400], [1000, 2000]]]
new_edges = edges.map { |edge| edge.map { |point| [808912] + point } }
# => [[[808912, 1, 2], [808912, 100, 200]], [[808912, 700, 400], [808912, 1000, 2000]]]
edges
# => [[[1, 2], [100, 200]], [[700, 400], [1000, 2000]]]

下面是一个更一般的情况,它说明了您遇到的问题以及纠正它的方法。假设我们有以下嵌套数组:

a0 = [1, 2]
a1 = [3, 4]
a = [a0, a1]
  #=> [[1, 2], [3, 4]] 
edges = [a]
  #=> [[[1, 2], [3, 4]]] 
a0
a1
a
具有唯一的对象ID:

edges.object_id           #=> 1208140 

a.object_id               #=> 1085620  
edges[0].object_id        #=> 1085620 

a0.object_id              #=> 0977080 
a[0].object_id            #=> 0977080
edges[0][0].object_id     #=> 0977080

a1.object_id              #=> 0995980 
a[1].object_id            #=> 0995980
edges[0][1].object_id     #=> 0995980
edges[0][1][0].object_id  #=> 7
new_edges.object_id          #=> 2400460 (different than edges.object_id)
new_edges[0].object_id       #=> 1085620 (same as edges[0].object_id)
new_edges[0][0].object_id    #=> 0977080 (same as edges[0][0].object_id)
new_edges[0][1].object_id    #=> 0995980 (same as edges[0][1].object_id)
new_edges[0][1][0].object_id #=> 7       (same as edges[0][1][0].object_id)    
为了便于阅读,我删除了每个对象ID的前七位数字,在所有情况下都是
4833847
。注意
边[0][1][0]#=>3
3.object#id=>7
。出于效率考虑,整数(和某些其他Ruby对象)具有固定的、较小的对象ID

现在使用以下方法从
边创建一个新数组:

检查对象ID(最后六位数字):

edges.object_id           #=> 1208140 

a.object_id               #=> 1085620  
edges[0].object_id        #=> 1085620 

a0.object_id              #=> 0977080 
a[0].object_id            #=> 0977080
edges[0][0].object_id     #=> 0977080

a1.object_id              #=> 0995980 
a[1].object_id            #=> 0995980
edges[0][1].object_id     #=> 0995980
edges[0][1][0].object_id  #=> 7
new_edges.object_id          #=> 2400460 (different than edges.object_id)
new_edges[0].object_id       #=> 1085620 (same as edges[0].object_id)
new_edges[0][0].object_id    #=> 0977080 (same as edges[0][0].object_id)
new_edges[0][1].object_id    #=> 0995980 (same as edges[0][1].object_id)
new_edges[0][1][0].object_id #=> 7       (same as edges[0][1][0].object_id)    
可以看出,
new_edges
是一个新对象,但其所有嵌套数组和元素都是与
edges
中相应的嵌套数组和元素相同的对象

现在,让我们执行以下操作:

edges[0][1][0] = 5
edges[0][1][0].object_id #=> 11
然后

新边
都已更改,因为
边[0][1]
新边[0][1]
是同一个对象(数组),我们刚刚更改了该对象的第一个元素

发生更改时,我们如何避免更改
新边

首先,请注意,
new_-edges=Array.new(edges)
可以替换为
new_-edges=edges.dup
。与以前一样,
edges
new_-edges
将是不同的对象,但它们对应的嵌套数组将是相同的对象

我们希望通过制作
边的深度副本来定义
新边
,以便后者的更改不会影响前者,反之亦然:

new_edges = edges.map { |a| a.map { |aa| aa.dup } }  
  #=> [[[1, 2], [3, 4]]]
new_edges.object_id       #=> 2134620 (different than edges.object_id) 
new_edges[0].object_id    #=> 2134600 (different than edges[0].object_id)
new_edges[0][0].object_id #=> 2134580 (different than edges[0][0].object_id)
new_edges[0][1].object_id #=> 2134560 (different than edges[0][1].object_id)
现在更改
中的嵌套元素,并观察
新边
的值:

edges[0][1][0] = 5

edges
  #=> [[[1, 2], [5, 4]]] 
new_edges
  #=> [[[1, 2], [3, 4]]]
可以看出,
新的_边
未被修改

如果存在更高级别的嵌套,则使用
map
dup
创建深度副本可能会变得单调乏味,并且容易出错。更简单的方法是使用and,它可以创建可能包含多个级别嵌套对象的各种Ruby对象的深度副本:

edges
  #=> [[[1, 2], [5, 4]]] 
new_edges = Marshal.load(Marshal.dump(edges))
  #=> [[[1, 2], [5, 4]]]
的更改现在将使
新边
不受影响

edges[0][1][0] = 3

edges
  #=> [[[1, 2], [3, 4]]] 
new_edges
  #=> [[[1, 2], [5, 4]]] 

看起来你两次都在创建
新边
数组。一次使用array.new,两次使用new\u edges.map。你为什么这样做?当你在终端中使用此代码时发生了什么?有更好的方法吗?你能给我一些建议吗,谢谢。好的,array.new(edges)似乎没问题……但问题是什么?运行此代码时,您需要包括结果,即错误消息或输出。可能与最初的结果重复:[[1,2],[100,200],[700,400],[1000,2000]],我想要的:[[808912,1,2],[808912,100,200],[808912,700,400],[808912,1000,2000]],我需要使用初始值laterRuby没有
deep\u dup
。ActiveSupport有(因此Rails也有).噢,我只是在谷歌上搜索了一下,一定是看错了文档。我的错,我刚刚看到了#deep_dup,但它一定是ActiveSupport而不是Ruby。而且,你的解决方案更节省空间和时间,哈哈。只使用一种不变异原始数组的方法比使用变异的方法对原始数组进行deep_dup要好得多。a在不使用Rails的情况下深度克隆数组的有点黑客的方法是
Marshal.load Marshal.dump(my_array)
Marshal.dump
将数组打包成字符串格式,然后
Marshal.load
解压成数组。谢谢。我还找到了另一种方法:“new_edges=marshall.load(marshall.dump(edges))”new_edges.map{| point | point.insert(0808912)}'
edges[0][1][0] = 3

edges
  #=> [[[1, 2], [3, 4]]] 
new_edges
  #=> [[[1, 2], [5, 4]]]