哈希似乎通过引用传递:Ruby

Hash appears to be passing by reference : Ruby

我知道标题不明确,所以让我们看看下面的例子。 我有一个名为 sh 的哈希,然后我将 sh 分配给一个新变量 su ,之后我正在修改 sh ,但是 su 也被修改了。我想 su 保留原始内容。

irb(main):001:0> sh = {"name"=>"shan", "age"=>"33" , "sex"=>"male"}
=> {"name"=>"shan", "age"=>"33", "sex"=>"male"}
irb(main):002:0> su = sh
=> {"name"=>"shan", "age"=>"33", "sex"=>"male"}
irb(main):003:0> su
=> {"name"=>"shan", "age"=>"33", "sex"=>"male"}

irb(main):005:0> sh.delete("sex")
=> "male"
irb(main):006:0> sh
=> {"name"=>"shan", "age"=>"33"}  => ok here
irb(main):007:0> su
=> {"name"=>"shan", "age"=>"33"}   => ??

irb(main):010:0> sh["city"] = "Bangalore"  => New example
=> "Bangalore"
irb(main):011:0> sh 
=> {"name"=>"shan", "age"=>"33", "city"=>"Bangalore"} =>  ok
irb(main):012:0> su 
=> {"name"=>"shan", "age"=>"33", "city"=>"Bangalore"}  => ??  

.delete 从内存中删除数据

你可以使用这个:su = Hash[sh]

clone 方法是 Ruby 进行浅拷贝的标准内置方法:

su = sh.clone

备注:以上解决方案仅适用于非嵌套哈希。在嵌套哈希的情况下,您需要编组

su = Marshal.load(Marshal.dump(sh))

如果通过引用或值传递,这不是问题。它只是指向同一个对象的两个变量,并且可以修改该对象(添加键、删除键、更改值)。

您可以使用 object_id

进行检查
3.0.0 :002 > sh.object_id
 => 260
3.0.0 :004 > su.object_id
 => 260

就是你程序中的这一行

su = sh

它告诉 Ruby 你希望你调用 sh 的哈希也被称为 su。有点叫我的名字或昵称:-)

并非 object_id 的输出可能因 ruby 版本和程序运行而异。

有多种方法可以拥有两个单独的哈希值(或其他对象:

使用初始化程序创建两个单独的对象:

sh = {"name"=>"shan", "age"=>"33" , "sex"=>"male"}
su = {"name"=>"shan", "age"=>"33" , "sex"=>"male"}

创建克隆:

sh = {"name"=>"shan", "age"=>"33" , "sex"=>"male"}
su = sh.clone

#clone 是为标准 ruby 类 定义的(例如字符串和数字)。如果您在哈希中存储其他内容,那么您可能需要自己实现它才能获得正确的副本。

3.0.0 :005 > su = sh.clone
3.0.0 :006 > sh.object_id
 => 260
3.0.0 :007 > su.object_id
 => 280

不过现在它变得有趣了。您只克隆了 Hash,而不是您存储在其中的对象:

sh = {a: "Hi"}
su = sh.clone
3.0.0 :010 > sh[:a].object_id
 => 300
3.0.0 :011 > su[:a].object_id
 => 300

这意味着如果您修改散列中的一个对象,它也会更改另一个散列引用的对象:

根据您的需要,您可以分配新对象:

sh = {a: "Hi"}
su = sh.clone

sh[:a] = "HI
sh[:a]
# => "HI"
su[:a] 
# => "Hi"

或就地修改它们

sh = {a: "Hi"}
su = sh.clone
sh[:a].upcase! # this changes the string inplace

sh[:a]
# => "HI"
su[:a]
# => "HI"

clone 制作一个浅拷贝,如果你想要一个完整的拷贝,在每个对象上调用克隆,你可以使用 Denny Mueller 描述的 Marshal load/dump 技巧或查看 ActiveSupports #deep_dup https://guides.rubyonrails.org/active_support_core_extensions.html#deep-dup

或者您可以自己编写一个实现并将其添加到您的散列中。

(那里也有一些宝石)