首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Ruby -按值传递

Ruby -按值传递
EN

Stack Overflow用户
提问于 2013-11-04 17:56:54
回答 3查看 1.2K关注 0票数 3

在Ruby中是否有通过值传递对象而不是通过引用传递对象的方法?例如,

代码语言:javascript
复制
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

输出应该是

代码语言:javascript
复制
Jack
Bob
Jack

而不是

代码语言:javascript
复制
Jack
Bob
Bob

任何帮助都将不胜感激。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-11-04 18:14:47

No. Ruby 按引用传递,而不是按值传递.

如果需要模拟按值传递,可以使用Ruby的Object#clone方法。在这种情况下,你会做这样的事情:

代码语言:javascript
复制
def get_name(obj)
  new_object = obj.clone
  new_object.name = "Bob"
  puts new_object.name
end

这使得对象的浅拷贝。换句话说,对象的实例变量被复制,但是变量引用的对象没有被复制。如果需要执行深度复制,则可以读取这个堆叠溢出柱。Ruby没有一种执行深度复制的方法,但这篇文章描述了如何使用编组和进行深度复制。

clonedup的工作原理非常相似,但有一些不同之处。根据医生的说法:

Object#clone

生成obj的浅拷贝--复制obj的实例变量,但不复制它们引用的对象。复制冰冻和污染状态的obj。还请参阅Object#dup下的讨论。

Object#dup

生成obj的浅拷贝--复制obj的实例变量,但不复制它们引用的对象。dup复制受污染状态的obj。还请参阅Object#clone下的讨论。一般来说,克隆和dup在子类中可能有不同的语义。当克隆用于复制对象(包括其内部状态)时,dup通常使用子代对象的类来创建新实例。 此方法可能具有特定于类的行为。如果是这样的话,这种行为将在类的#initialize_copy方法下记录下来。

您可以查看dup无性系文档。

编辑

虽然我的回答可能给出了OP所要寻找的东西,但对于通过引用或值传递的语义而言,这并不是完全正确的。有关更多的讨论,请参阅此页面上的其他答案和评论。您还可以查看注释这里这个职位中的讨论以获得更多信息。

票数 5
EN

Stack Overflow用户

发布于 2013-11-04 18:26:32

在“Michael的精彩答案”之外,正式回答原来的问题:

在Ruby中是否有通过值传递对象而不是通过引用传递对象的方法?

不是的。绝对不是。不可能。不,算了吧。

或者更准确地说,如果我们挑剔词汇表,Ruby会通过值传递对对象的引用,因此传递的对象是可变的。

在Ruby中,您必须要么:

  1. 根据需要手动克隆/dup输入和输出(见Michael的答案);或
  2. 假设如果你决定不这样做的话,没有人会搞砸你班级的内部结构。

Rubyist倾向于选择选项2,因为内部构件是以这种方式构建的(attr_reader等返回对象状态),因为性能方面的原因,以及创建您返回或操作的所有内容的深度副本在实践中绝非易事。

在回答约格的评论时:

代码语言:javascript
复制
def foo(bar) bar << ' is now mutated' end;
baz = 'Baz';
foo(baz);
puts baz # Baz is now mutated

增加一个关于冻结的说明/例子,因为这也会产生意想不到的结果:

代码语言:javascript
复制
foo = {foo: 'bar'}.freeze  # {:foo=>"bar"}
foo[:foo] += 'baz'         # RuntimeError: can't modify frozen Hash
foo[:foo] << 'baz'         # {:foo=>"barbaz"}
票数 2
EN

Stack Overflow用户

发布于 2013-11-05 11:39:47

Ruby是按值传递的。一直都是。下面是一个简单的程序,说明了这一事实:

代码语言:javascript
复制
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”,如果我剪掉我的头发,你们都会看到的。

如果不希望对象发生变异,则需要使其不可变。例如:

代码语言:javascript
复制
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对象显然引用的对象仍然可以更改:

代码语言:javascript
复制
jack.name << ' the Ripper'

puts jack.name
# Jack the Ripper

jack.name.replace('Bob')

puts jack.name
# Bob

如果您想要对此进行保护,则需要确保Person对象引用的所有对象也是不可变的。例如:

代码语言:javascript
复制
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做其他事情,那么它们就会有一个令人讨厌的惊喜:

代码语言:javascript
复制
name = 'Jack'

jack = Person.new(name)

name << ' the Ripper'
# RuntimeError: can't modify frozen String

我们可以先创建String的副本,然后再对其进行freeze处理,从而解决这个问题:

代码语言:javascript
复制
class Person
  def initialize(name)
    self.name = name.dup.freeze
  end
end

name = 'Jack'

jack = Person.new(name)

name << ' the Ripper'
# => 'Jack the Ripper'
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19774021

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档