在Ruby中是否有通过值传递对象而不是通过引用传递对象的方法?例如,
class Person
attr_accessor :name
end
def get_name(obj)
obj.name = "Bob"
puts obj.name
end
jack = Person.new
jack.name = "Jack"
puts jack.name
get_name(jack)
puts jack.name输出应该是
Jack
Bob
Jack而不是
Jack
Bob
Bob任何帮助都将不胜感激。
发布于 2013-11-04 18:14:47
No. Ruby 按引用传递,而不是按值传递.
如果需要模拟按值传递,可以使用Ruby的Object#clone方法。在这种情况下,你会做这样的事情:
def get_name(obj)
new_object = obj.clone
new_object.name = "Bob"
puts new_object.name
end这使得对象的浅拷贝。换句话说,对象的实例变量被复制,但是变量引用的对象没有被复制。如果需要执行深度复制,则可以读取这个堆叠溢出柱。Ruby没有一种执行深度复制的方法,但这篇文章描述了如何使用编组和进行深度复制。
clone和dup的工作原理非常相似,但有一些不同之处。根据医生的说法:
Object#clone
生成obj的浅拷贝--复制obj的实例变量,但不复制它们引用的对象。复制冰冻和污染状态的obj。还请参阅Object#dup下的讨论。
Object#dup
生成obj的浅拷贝--复制obj的实例变量,但不复制它们引用的对象。dup复制受污染状态的obj。还请参阅Object#clone下的讨论。一般来说,克隆和dup在子类中可能有不同的语义。当克隆用于复制对象(包括其内部状态)时,dup通常使用子代对象的类来创建新实例。 此方法可能具有特定于类的行为。如果是这样的话,这种行为将在类的#initialize_copy方法下记录下来。
您可以查看dup和无性系文档。
编辑
虽然我的回答可能给出了OP所要寻找的东西,但对于通过引用或值传递的语义而言,这并不是完全正确的。有关更多的讨论,请参阅此页面上的其他答案和评论。您还可以查看注释这里和这个职位中的讨论以获得更多信息。
发布于 2013-11-04 18:26:32
在“Michael的精彩答案”之外,正式回答原来的问题:
在Ruby中是否有通过值传递对象而不是通过引用传递对象的方法?
不是的。绝对不是。不可能。不,算了吧。
或者更准确地说,如果我们挑剔词汇表,Ruby会通过值传递对对象的引用,因此传递的对象是可变的。
在Ruby中,您必须要么:
Rubyist倾向于选择选项2,因为内部构件是以这种方式构建的(attr_reader等返回对象状态),因为性能方面的原因,以及创建您返回或操作的所有内容的深度副本在实践中绝非易事。
在回答约格的评论时:
def foo(bar) bar << ' is now mutated' end;
baz = 'Baz';
foo(baz);
puts baz # Baz is now mutated增加一个关于冻结的说明/例子,因为这也会产生意想不到的结果:
foo = {foo: 'bar'}.freeze # {:foo=>"bar"}
foo[:foo] += 'baz' # RuntimeError: can't modify frozen Hash
foo[:foo] << 'baz' # {:foo=>"barbaz"}发布于 2013-11-05 11:39:47
Ruby是按值传递的。一直都是。下面是一个简单的程序,说明了这一事实:
def foo(bar)
bar = 'reference'
end
baz = 'value'
foo(baz)
puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value您所看到的只是简单的共享可变状态:如果一个对象是可变的,它可以变异,如果该对象是通过多个引用可见的,那么该突变将通过多个引用可见。就这么简单。如果你像我朋友那样叫我“J rg”,或者像我妈妈那样叫“儿子”,不管你叫我“J rg”,如果我剪掉我的头发,你们都会看到的。
如果不希望对象发生变异,则需要使其不可变。例如:
class Person
attr_reader :name
private
attr_writer :name
def initialize(name)
self.name = name
end
end
def get_name(obj)
obj.name = "Bob"
puts obj.name
end
jack = Person.new('Jack')
puts jack.name
# Jack
get_name(jack)
# NoMethodError: private method `name=' called for #<Person:0xdeadbeef081542 @name='Jack'>
puts jack.name
# Jack现在,您的Person对象不能再被其他对象更改。但是,请注意,Person对象显然引用的对象仍然可以更改:
jack.name << ' the Ripper'
puts jack.name
# Jack the Ripper
jack.name.replace('Bob')
puts jack.name
# Bob如果您想要对此进行保护,则需要确保Person对象引用的所有对象也是不可变的。例如:
class Person
def initialize(name)
self.name = name.freeze
end
end
jack = Person.new('Jack)
jack.name << 'the Ripper'
# RuntimeError: can't modify frozen String现在,您的Person对象确实是不可变的。(至少在具有Ruby这样强大的反射功能的语言中是“真正的”。)
不幸的是,我们现在对其他人做了同样的事情,我们试图保护自己不受伤害:我们正在改变他们的目标!在Person#initialize中,我们对通过freeze传入的String进行变异。如果创建Person的方法想要使用String做其他事情,那么它们就会有一个令人讨厌的惊喜:
name = 'Jack'
jack = Person.new(name)
name << ' the Ripper'
# RuntimeError: can't modify frozen String我们可以先创建String的副本,然后再对其进行freeze处理,从而解决这个问题:
class Person
def initialize(name)
self.name = name.dup.freeze
end
end
name = 'Jack'
jack = Person.new(name)
name << ' the Ripper'
# => 'Jack the Ripper'https://stackoverflow.com/questions/19774021
复制相似问题