5

我想跟踪一些任意命名的字符串的计数,然后将计数重置为零。我的想法是执行以下操作:

reset_hash={"string1"=>0,"string2"=>0,"string3"=>0}
=> {"string1"=>0, "string2"=>0, "string3"=>0} 

new_hash = reset_hash
=> {"string1"=>0, "string2"=>0, "string3"=>0} 

new_hash["string1"]=1
new_hash["string3"]=1
new_hash
=> {"string1"=>1, "string2"=>0, "string3"=>1}

...

现在我想将 new_hash 重置为 reset_hash:

new_hash = reset_hash
=> {"string1"=>1, "string2"=>0, "string3"=>1}
reset_hash
=> {"string1"=>1, "string2"=>0, "string3"=>1} 

这里发生了什么?看来reset_hash实际上已经设置为new_hash,这与我想要的相反。如何实现所需的行为?

4

4 回答 4

5

正如其他人提到的,您必须使用clone。您的任务应如下所示:

reset_hash={"string1"=>0,"string2"=>0,"string3"=>0}
new_hash = reset_hash.clone

new_hash["string1"]=1
new_hash["string3"]=1
new_hash

new_hash = reset_hash.clone
reset_hash
于 2012-07-16T20:28:36.237 回答
5

当你有一个指向一个对象的变量时,你实际上只是有一个对该对象的引用。如果 a 和 b 都指向哈希 {1=>3, "foo" => 54},则更改 a 或 b 将更改另一个。

但是,您可以结合使用两种方法来制作您想要的东西。

哈希的默认值:

new_hash = Hash.new(0)

这为未使用的值提供了默认值 0:

new_hash["eggs"]  # -> 0

然后,您可以添加计数:

new_hash["string1"] += 1 # => 1

完成后只需致电

new_hash.clear # => {}

并且您的哈希将被重置,但新访问仍将默认为 0。

请注意,如果您将默认值设置为数字以外的对象类型,您可能会因为上面提到的整个参考问题而进行更改。

irb(main):031:0> b = Hash.new("Foo") #=> {}
irb(main):032:0> b[3] #=> "Foo"
irb(main):033:0> b[33] #=> "Foo"
irb(main):034:0> b[33].upcase! #=> "FOO"
irb(main):035:0> b[3] # => "FOO"

为了解决这个问题,您可以将一个块传递给您的哈希:

h = Hash.new {|hash, key| hash[key] = "new default value"}

这每次都会在 key 处创建一个新对象,因此对一个对象的更改不会产生涟漪:

d = Hash.new{ |hash,key| hash[key] = "string"} #=> {}
d[3] # => "string"
d[3].upcase! #=> "STRING"
d[5] #=> "string"
于 2012-07-16T20:25:24.310 回答
2

您正在修改单个哈希。

两个变量都引用同一个哈希。当您更改散列中的项目时,两个引用都将反映该更改 - 因为它是相同的散列实例。

也许您想先复制哈希?如果你这样做了,并且你有一个包含复杂对象的哈希,你还需要研究浅拷贝和深拷贝/克隆。

于 2012-07-16T20:16:35.843 回答
1

您需要使用克隆来制作副本。

https://stackoverflow.com/a/4157438/1118101

否则,您只会创建 2 个指向同一个哈希的“指针”,而不是复制内容。

然后使用 replace 将克隆的内容复制回您现有的哈希中。

于 2012-07-16T20:17:26.157 回答