为什么<< b的行为与a = a + b不同(复制的带字符串的哈希数组)

时间:2015-07-02 19:11:09

标签: ruby dup

我试图在不更改原始数组的情况下修改数组的副本。它是一系列哈希,所以要创造一个全新的"我使用的数组的副本:

foo = [ { :a => "aaaaaa" } ]
foocopy = foo.map { |h| h.dup }

我想将一些数据附加到副本中哈希的字符串中。

如果我使用=+

,它可以正常工作
foocopy.first[:a] = foocopy.first[:a] + "bbbbb"
foo
=> [{:a=>"aaaaaa"}]  # original unchanged as expected
foocopy
=> [{:a=>"aaaaaabbbbb"}]

但是,如果我使用<<,它会修改副本和原始文件:

foocopy.first[:a] << "cccccc"
foo
=> [{:a=>"aaaaaacccccc"}]   # ORIGINAL got changed too
foocopy
=> [{:a=>"aaaaaacccccc"}]

这是Ruby中的错误吗?

2 个答案:

答案 0 :(得分:2)

不,这是因为你复制了数组和散列,但是字符串是一个具有相同id的对象,因为ruby以奇怪的方式处理字符串。

irb(main):001:0> foo = [ { :a => "aaaaaa" } ]
=> [{:a=>"aaaaaa"}]
irb(main):002:0> foocopy = foo.map { |h| h.dup }
=> [{:a=>"aaaaaa"}]
irb(main):003:0> foo.object_id
=> 70252221980900
irb(main):004:0> foocopy.object_id
=> 70252221915920
irb(main):005:0> foocopy.first.object_id
=> 70252221915880
irb(main):006:0> foo.first.object_id
=> 70252221980940
irb(main):007:0> foocopy.first[:a].object_id
=> 70252221980960
irb(main):008:0> foo.first[:a].object_id
=> 70252221980960

这意味着:a+b将此对象重新实例化为更改的内容,a << b修改对象的实例。这是实际的方法行为。

只需使用字符串:

irb(main):009:0> a = "test"
=> "test"
irb(main):010:0> b = a.dup
=> "test"
irb(main):011:0> a.object_id
=> 70252221685660
irb(main):012:0> b.object_id
=> 70252221662100
irb(main):013:0> a = a + "1"
=> "test1"
irb(main):014:0> a.object_id
=> 70252221586140
irb(main):015:0> b << "1"
=> "test1"
irb(main):016:0> b.object_id
=> 70252221662100

来自文档:

http://ruby-doc.org/core-2.2.0/String.html#method-i-2B

http://ruby-doc.org/core-2.2.0/String.html#method-i-3C-3C

答案 1 :(得分:2)

dup执行&#34;浅拷贝&#34;一个对象。因此,您正在创建一个具有相同键和值的新Hash!不幸的是,Ruby并没有一个很好的内置方法来创建一个&#34;深层拷贝&#34;哈希,其中也复制了所有引用的对象。那你该怎么办?

我认为您已经找到了最佳解决方案,即使用+=。这是因为+创建了一个新对象,而=会覆盖复制的对象。

但是在Ruby中深度复制对象有一个简单的方法,即使用Marshal对其进行序列化/反序列化。

foo = [ { :a => "aaaaaa" } ]
foocopy = Marshal.load(Marshal.dump(foo))

然后,由于跨对象共享指针,您不会有任何意外。您的<<代码将按预期运行。