2

我有 Ruby 代码:

def test_111(hash)
  n = nil
  3.times do |c|
      if n
       n[c] = c
      else
       n = hash
      end
  end
end

a = {}
test_111(a)
p a

为什么它打印 {1=>1, 2=>2}而不是 {}??

在test_111方法中,hasha使用同一个内存?

如何在 test_111 方法中更改a值?

我无法理解

4

2 回答 2

4

哈希是通过引用传递的。因此,当您更改方法参数(这是一个哈希)时,您会更改原始哈希。

为避免这种情况,您应该克隆哈希。

test_111(a.dup)

这将创建一个浅拷贝(也就是说,它不会克隆您可能拥有的子哈希)。

浅拷贝是什么的一个小例子:

def mutate hash
  hash[:new] = 1
  hash[:existing][:value] = 2
  hash
end

h = {existing: {value: 1}}

mutate h # => {:existing=>{:value=>2}, :new=>1}
# new member added, existing member changed
h # => {:existing=>{:value=>2}, :new=>1}



h = {existing: {value: 1}}

mutate h.dup # => {:existing=>{:value=>2}, :new=>1}
# existing member changed, no new members
h # => {:existing=>{:value=>2}}
于 2012-04-22T12:50:17.970 回答
0

在 ruby​​ 中,几乎每个对象都是通过引用传递的。这意味着当你做一些简单的事情时

a = b

除非 a 是简单类型之一,否则在此分配之后ab将指向同一事物。

这意味着如果你改变第二个变量,第一个变量也会受到同样的影响:

irb(main):001:0> x = "a string"
=> "a string"
irb(main):002:0> y = x
=> "a string"
irb(main):003:0> x[1,0] = "nother"
=> "nother"
irb(main):004:0> x
=> "another string"
irb(main):005:0> y
=> "another string"
irb(main):006:0> 

当然这同样适用于哈希:

irb(main):006:0> a = { :a => 1 }
=> {:a=>1}
irb(main):007:0> b = a
=> {:a=>1}
irb(main):008:0> a[:b] = 2
=> 2
irb(main):009:0> a
=> {:a=>1, :b=>2}
irb(main):010:0> b
=> {:a=>1, :b=>2}
irb(main):011:0> 

如果您不希望发生这种情况,请使用.dupor .clone

irb(main):001:0> a = "a string"
=> "a string"
irb(main):002:0> b = a.dup
=> "a string"
irb(main):003:0> a[1,0] = "nother"
=> "nother"
irb(main):004:0> a
=> "another string"
irb(main):005:0> b
=> "a string"
irb(main):006:0> 

对大多数人和dupclone同样的效果。

因此,如果您编写一个修改其参数之一的函数,除非您特别希望调用该函数的代码可以看到这些更改,否则您应该首先复制正在修改的参数:

def test_111(hash)
  hash = hash.dup
  # etc
end

代码的行为称为副作用- 对程序状态的更改不是函数的核心部分。一般要避免副作用。

于 2012-04-22T13:06:32.427 回答