2013-11-09 2 views
0

Я не уверен, что происходит, когда вы передаете объект модульному методу. Проходит ли объект по ссылке или путем копирования? Как и в этом примере:Передавать по ссылке - Ruby Modules

module SampleModule 
    def self.testing(o) 
    o.test 
    end 
end 

class SampleClass 
    def initialize(a) 
    @a = a 
    end 

    def test 
    @a = @a + 1 
    end 
end 

sample_object = SampleClass.new(2) 
3.times do 
    SampleModule.testing(sample_object) 
end 
p sample_object # => #<SampleClass:somehexvalue @a=5> 

похоже передается. На самом деле неясно.

ответ

2

Все Переменные в Ruby - это ссылки на объекты. Вы не можете «передавать по значению» по сравнению с «передавать по ссылке» таким же образом, как у вас есть этот выбор на C, C++ или Perl. Фактически, Ruby силы проходят по значению, нет других вариантов. Однако отправляемые значения всегда являются ссылками на объекты. Это немного похоже на использование C или C++, где все переменные-члены являются указателями или используются Perl, где вы всегда должны работать со ссылками, даже при работе с простыми скалярами.

Я думаю, что это разделение переменной от данных объекта, которая вас сбивает с толку.

Несколько моментов:

  • Variable распределения никогда чрезмерно пишут другие переменные, которые могут указывать на тот же объект. Это в значительной степени определение pass-by-value. Однако это не соответствует вашим ожиданиям, что содержимое объектов также защищено.

  • Переменные и элементы в контейнерах (например, в Array с и String с) отдельные переменные, и если вы отправляете контейнер, может изменить его содержание непосредственно, потому что вы послали ссылку на контейнер, и который включает такие же переменные для его содержимого. Я думаю, что это то, что вы имеете в виду «как представляется, передача по ссылке»

  • Некоторых классов - в том числе тех представлений чисел, и Symbol - неизменны, т.е. нет никаких изменений на месте методы для числа 4. Но концептуально вы по-прежнему передаете ссылку на особый объект 4 в рутину (под капотом, для эффективности Ruby будет иметь значение 4, закодированное просто в распределении памяти переменной, но это деталь реализации - это также значение " указатель "в этом случае).

  • Самый простой способ получить близко к «проходят по значению» семантикой вы, кажется, ищет с SampleModule является clone параметров в начале процедуры.Обратите внимание: это не приводит к тому, что Ruby изменяет семантику вызова, только в этом случае извне метода вы получаете безопасное предположение (независимо от того, что происходит с параметром внутри метода остается внутри метода), которое вам кажется нужным:


module SampleModule 
    def self.testing(o) 
    o = o.clone 
    o.test 
    end 
end 
  • Технически это должно быть глубоко клоном, чтобы быть универсальными, но это не требуется, чтобы сделать ваш пример работу близко к пропуску по значению. Вы можете позвонить SampleModule.testing(any_var_or_expression) и знать, что все, что any_var_or_expression находится в остальной части вашего кода, связанный с ним объект не будет изменен.
+0

Возможно, вы можете уточнить, что 'o .clone' не _pass по значению_, но _ создает новый объект, копирующий переменные экземпляра клонированного объекта и возвращает it_; это означает, что переменные экземпляра нового объекта ссылаются на соответствующие переменные экземпляра клонированного объекта, что является решающим отличием от концепции «пройти по значению» – mdesantis

+1

@ProgNOMmers - Да, я думаю, что я хотел использовать «глубокий клон», и мой пример работает только потому, что атрибут является числовым. С глубоким клоном вы становитесь ближе к поведению «pass by value» в принципе (так как во всем, что вы делаете с параметром внутри метода, не важно для вызывающего), хотя на самом деле, когда у вас есть глубокое дерево ссылок, это не такая чистая концепция в первую очередь. –

+0

Это вещь, которую я пропускаю в Ruby: нет четкого способа сделать глубокий дубль объектов; в большинстве случаев вы должны реализовать его самостоятельно. – mdesantis

-1

Все в Ruby, передается по ссылке:

class Test 
    attr_reader :a 
    def initialize(a) 
    @a = a 
    end 
end 

s = "foo" 
o = Test.new(s) 
o.a << "bar" 
o.a   #=> "foobar" 
s   #=> "foobar" 
o.a.equal? s #=> true 

В своем коде, тот факт, что вы передаете объект в метод модуля ничего не меняет; sample_object - ссылка на новый объект SampleClass.new(2)

+0

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

+0

@ JörgWMittag, я получил шутку, но downvote ответ был некрасивым ... – mdesantis

2

Если вы действительно хотите быть анальными на лексике, Ruby передает ссылки на (изменяемые) объекты по значению:

def pass_it(obj) 
    obj = 'by reference' 
end 

def mutate_it(obj) 
    obj << ' mutated' 
end 

str = 'by value' 

pass_it(str) 
mutate_it(str) 

puts str # by value mutated 

Вы можете работать вокруг проблемы, которые могут возникнуть в связи с этим с помощью dup или clone (обратите внимание, что оба делают мелкие копии) и freeze.

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