在 Ruby 中如何通过引用传递参数?

30
在Ruby中,是否可以传递一个值类型语义的参数(例如Fixnum),以引用的方式传递?类似于C#中的'ref'关键字。
示例:
在Ruby中,是否可以像C#的“ref”关键字一样通过引用传递具有值类型语义的参数(例如Fixnum)?
示例:
def func(x) 
    x += 1
end

a = 5
func(a)  #this should be something like func(ref a)
puts a   #should read '6'

顺便说一句,我知道我可以使用:

a = func(a)
6个回答

32

您可以通过显式传递当前绑定来实现此操作:

def func(x, bdg)
  eval "#{x} += 1", bdg
end

a = 5
func(:a, binding)
puts a # => 6

Ruby绑定的文档。 - Cristian Diaconescu
这是一个可怕的补丁:涉及各种错别字、安全和丑陋的风险。最好使用某种包装器,将“绑定”留给像“pry”这样的东西。 - Tim Baverstock

20

Ruby不支持完全的“按引用传递”。所有东西都是对象,对这些对象的引用始终通过值传递。实际上,在您的示例中,您正在通过值传递引用的副本到Fixnum对象。

你的代码问题在于,x += 1并没有修改传递的Fixnum对象,而是创建了一个全新且独立的对象。

我认为,Java程序员会将Fixnum对象称为不可变的(immutable)


4
Ruby不采用按引用传递的方式,而变量和参数仅能包含对对象的引用。说引用是按值传递的意思等同于说对象是按引用传递的。我不理解这个问题。 - Damien Pollet
2
@Damien:不是这样的。按引用传递(例如在C++、C#、PHP、Perl中)允许您像在外部作用域中分配变量一样分配变量。如果您使用真正的按引用传递,您尝试在这里做的事情是可能的;但不能通过引用传递的值来实现。 - newacct
8
没错,我正在重新阅读Christoph的回答和我的评论,我不再同意我2009年时的看法。嗯... - Damien Pollet

12

Ruby中无法通过引用传递参数。对于您的示例,您必须返回新值并将其分配给变量a或创建一个包含该值的新类,并传递该类的实例。例如:

class Container
attr_accessor :value
 def initialize value
   @value = value
 end
end

def func(x)
  x.value += 1
end

a = Container.new(5)
func(a)
puts a.value

8
您可以尝试以下技巧:
def func(x) 
    x[0] += 1
end

a = [5]
func(a)  #this should be something like func(ref a)
puts a[0]   #should read '6'

在技术上,这也替换了写操作中的变量(以及列表中的引用)。 - itarato
@itarato a和x在它们的生命周期内都指向同一个列表,因此没有任何变量替换,但是该列表的第一个条目被更改为指向不同的数字。我喜欢这个技巧,因为使用列表感觉比包装类的.get/.set方法少了一些心理负担,但这意味着您需要记住使用[0]来取消引用它。 - Tim Baverstock

2

-1

然而,似乎复合对象(如哈希)是通过引用传递的:

fp = {}
def changeit(par)
  par[:abc] = 'cde'
end

changeit(fp)

p fp

提供

{:abc=>"cde"}

哈希表是按值传递的,但该值是对可变对象的引用。因此,fp始终指向同一哈希对象实例,但changeit修改了其内容。您可以通过打印fp.object_id和par.object_id来确认这一点。您不能仅通过将不同的哈希分配给par来影响fp的值 - 这就是“按引用传递”所允许的。 - Tim Baverstock

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接