2012-04-22 3 views
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, то хэш и использовать ту же память?

Как можно изменить значение a в методе test_111?

Я не могу понять

ответ

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}} 
+0

Jörg W Mittag считает, что Ruby является пропускной стоимостью. http://stackoverflow.com/a/6528257/38765 –

+0

Он также признает, что пройденные значения могут быть указателями. С точки зрения плохого кодера это ссылка :) –

0

В рубин, почти каждый объект передается по ссылке. Это означает, что когда вы делаете что-то же просто, как

a = b 

, если только не был один из самых простых типов, после этого назначения a и b будет указывать на то же самое.

Это означает, что если вы изменяете вторую переменную, то первая влияет таким же образом:

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> 

Если вы не хотите чтобы это произошло, используйте .dup или .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> 

для мо st people dup и clone имеют такой же эффект.

Так что, если вы пишете функцию, которая изменяет один из его параметров, если вы специально не хотите, чтобы эти изменения будут рассматриваться в коде, который вызывает функцию, вы должны сначала DUP параметр модифицируется:

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

Поведение вашего кода называется побочным эффектом - изменение состояния программы, которое не является основной частью функции. Побочных эффектов, как правило, следует избегать.

Смежные вопросы