2012-05-24 3 views
1

Я написал два рубин файлов для тестированияЧто-то запутанное о рубине параметр обходя

test.rb:

#!/usr/bin/ruby 

def foo(bar) 
    bar['key'] = 'value' 
end 

def my_print(a) 
    a.each{|k,v| 
     puts "#{k} => #{v}" 
    } 
end 

test_drive.rb:

#!/usr/bin/ruby 

require 'test.rb' 

hash_test = Hash.new 

foo(hash_test) 

my_print(hash_test) 

Он работает как то, что я ожидаются, выход

ключ => значение

Но когда я chanege в test.rb к

#!/usr/bin/ruby 

def foo(bar) 
    pre_defined = {'key' => 'value'} 
    bar = pre_defined 
end 

def my_print(a) 
    a.each{|k,v| 
     puts "#{k} => #{v}" 
    } 
end 

Здесь я использовал предопределенный хеш, но теперь она не выводит ничего. «Hash_test» теперь представляет собой пустой хеш. Пожалуйста, проиллюстрируйте меня, почему это действительно так происходит?

ответ

3

Вот простой ответ:

В рубина вы передаете переменные с объектами по ссылке. Когда вы назначаете Object переменной, эта переменная не содержит в себе самого объекта. Вместо этого он содержит только ссылку на этот объект.

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

Важно понять, что параметр bar в методе foo действительно является лишь ссылкой на объект в памяти, а не сам объект. Поэтому, если бар однажды указал на объект, но теперь ссылается на другой объект, он будет изменять то, на что он ссылается, а не предыдущий объект, на который он указал. Вот код последнего test.rb прокомментировал немного для вас, чтобы лучше понять его:

def foo(bar) # here "bar" points to the same object as "hash_test" 
    pre_defined = {'key' => 'value'} # here a new object is created and referenced 
    bar = pre_defined # now "bar" points to a new object and forgets about "hash_test" 
end 

def my_print(a) # here "a" holds a reference to "hash_test" which is empty 
    a.each{|k,v| # "a" is empty, so it has nothing to put 
     puts "#{k} => #{v}" 
    } 
end 

Я надеюсь, что это помогает. Если вам нужно что-то более подробное:

Причина, по которой ваша последняя версия test.rb печатает пустой хэш в test_drive.rb, состоит в том, что объект Hash, на который указывает ссылка «hash_test», на самом деле не изменяется на все.Вместо этого метод foo, в то время как первоначально получая в качестве параметра под названием «bar» ссылку на объект Hash, на который указывает «hash_test», быстро заменяет эту ссылку в «баре» новой ссылкой на новый Hash Object, pre_defined "переменная указывает на. Теперь «bar» не указывает на тот же объект, что и «hash_test», и поэтому объект, который указывает «hash_test», никогда не изменяется. Таким образом, объект Hash, содержащий «hash_test», никогда не заполняется ничем и пуст, когда my_print пытается его распечатать.

+0

Спасибо! Это действительно помогает. Но сказано, что переменная, тип/класс которой является fixnum, true/false и т. Д., Содержит в ней значение. Это правильно? – fabregaszy

+0

Как я понял, в Ruby у вас нет примитивных типов. Переменная, которая считается типом класса Fixnum, на самом деле является ссылкой на экземпляр Fixnum, который представляет собой целое число. Ruby реализован на C, поэтому примитивы do существуют за кулисами, но нет способа получить доступ к чистым примитивным типам в коде Ruby, насколько я знаю. Перейдите в irb и попробуйте a = 5 и b = 5, затем выполните a.object_id. Они оба относятся к одному и тому же объекту: a.object_id, b.object_id, try 5.object_id, а также 5.class, 5. методы. – Andres

+0

Просто для подтверждения, Fixnum и True/False являются объектами. Просто попробуйте этот небольшой фрагмент в irb для подтверждения: 5.class.ancestors, а также true.class.ancestors и false.class.ancestors – Andres

1

То есть, поскольку Ruby проходит по значению, посредством чего передается ссылка объекта. Это похоже на то, что делает Java. Это означает, что у вас есть только копия ссылки в foo-методе и переназначение ее не меняет ссылку вне этого метода.

Подробнее: В вашем первом примере вы передаете хэш вашему foo-методу. Ссылка будет скопирована, поэтому внутри foo-метода у вас есть копия ссылки, которая указывает на тот же объект, что и «hash_test», но не является тем же. Поэтому вызов методов Hash изменяет значения объекта вне метода. Но в вашем втором примере вы не меняете значения данного хэша, вы назначаете новый объект Hash копии ссылки метода, которая не влияет на ссылку «hash_test».

+1

И из-за этого Ruby не «передает по значению», а «передает по ссылке». –

+1

@HolgerJust: Ruby is * not * pass-by-reference, это pass-by-value. Всегда. Без исключений. Если бы он был * передачей по ссылке, то у нас не было бы этого разговора, потому что код OP работал бы так, как он ожидал, и он никогда бы не задал этот вопрос. Не верьте мне? Спросите Ruby: 'def foo (bar) bar = 'reference' end; baz = 'value'; Foo (БАЗ); puts «Ruby is pass-by - # {baz}" ' –

+1

Ну, в вашем методе вы назначаете' baz' новой переменной и не обновляете ссылку. Попробуйте это вместо этого (которое сохраняет ссылку): 'def foo (bar) bar.replace ('reference'); конец; baz = 'value'; Foo (БАЗ); puts «Ruby is pass-by - # {baz}" ' –

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