Ruby是按值传递还是按引用传递?

时间:2013-05-09 08:16:55

标签: java ruby oop pass-by-reference pass-by-value

我基本上是一名java开发人员。我在红宝石工作了大约一年。与java不同,Ruby是一种纯粹的面向对象的编程语言。这是一个疑问。它是按值传递还是按引用传递? Java作为pass-by-value工作:“当传递基元时,我看到该值被复制并传递给方法。但是如果有对象,则引用被复制并传递给方法。引用包含对象的位置在堆中。在方法调用期间,只传递对象的位置。因此不会创建重复的对象。同样的对象被修改。“

但是当我尝试下面的ruby代码片段时,我得到了与Java相同的结果:“在方法调用期间,数字的工作方式类似于原语(如java中),而数组在java中的工作方式非常完美”。现在,我很困惑。如果ruby中的所有内容都是对象,那么在方法调用期间为什么数字对象会重复?

class A
  def meth1(a)
    a = a+5
    puts "a inside meth1---#{a}"
  end

  def meth2(array)
    array.pop
    puts "array inside meth2---#{array}"
  end
end


obj1 = A.new
aa=5
obj1.meth1(aa)
puts "aa-----#{aa}"

arr = [3,4,5]
obj1.meth2(arr)
puts "arr---#{arr}"

结果:

内部meth1 --- 10

AA ----- 5

数组里面的meth2 --- 34

ARR --- 34

4 个答案:

答案 0 :(得分:19)

Ruby使用pass-by-value,或者更确切地说,是一个传递值的特殊情况,其中传递的值是总是一个指针。这种特殊情况有时也称为分享呼叫,按对象分享或逐个呼叫。

它与Java(用于对象),C#(默认情况下用于引用类型),Smalltalk,Python,ECMAScript / JavaScript以及或多或少的所有面向对象语言使用的约定相同。

注意:在所有现有的Ruby实现Symbol上,FixnumFloat实际上是按直接传递的,而不是带有中间指针。但是,由于这三个是不可变的,在这种情况下,传值和对象共享之间没有可观察到的行为差异,因此您可以通过简单地处理所有内容来大大简化您的心理模型作为逐个对象分享。只需将这三种特殊情况解释为内部编译器优化,您无需担心。

这是一个简单的例子,您可以运行以确定传递Ruby(或任何其他语言,在您翻译之后)的约定的参数:

def is_ruby_pass_by_value?(foo)
  foo.replace('More precisely, it is call-by-object-sharing!')
  foo = 'No, Ruby is pass-by-reference.'
  return nil
end

bar = 'Yes, of course, Ruby *is* pass-by-value!'

is_ruby_pass_by_value?(bar)

p bar
# 'More precisely, it is call-by-object-sharing!'

答案 1 :(得分:6)

见下文,Object_id将回答您的所有问题:

class A
 def meth1(a)
  p a.object_id #=> 11
  a = a+5 # you passed a reference to the object `5`,but by `+` you created a new object `10`.
  p a.object_id #=> 21
 end

 def meth2(array)
  p array.object_id #=> 6919920
  array.pop
  p array.object_id #=> 6919920
 end
end


obj1 = A.new
aa=5
obj1.meth1(aa)
p aa.object_id #=> 11

arr = [3,4,5]
obj1.meth2(arr)
p arr.object_id #=> 6919920

在代码object reference is passed, by value中确实如此。注意+创建一个新对象,因此引用是10在本地进行的更改方法。

答案 2 :(得分:6)

它在两种情况下都是传值,比如Java。不同之处在于测试中的两个项都是对象,而在Java中,一个是原始的,另一个是对象。但无论某种东西是原始的还是对象,都没有影响传值和传递参考。按值传递与按引用传递有关,被调用的方法可以对传递给它的调用上下文中的变量执行操作。

让我们忽略语言和对象,然后看看传值与传递实际意味着什么。我将在模糊的B / Java / C / C ++ / C#/ D语法中使用伪代码:

Function Foo(arg) {
  arg = 6
}

declare variable a
a = 5
Foo(a)
output a

如果按值传递a,则输出为5.如果a通过引用传递(引用到变量a被赋予Foo),输出为6,因为Foo通过对变量的引用来处理a

请注意,两项测试之间存在很大差异。

在第一次测试中,您要为a分配一个全新的值:

a = a + 5

您不是修改传递给方法的a版本,而是使用该值将 new 值分配给{{1 }}

在第二次测试中,您只需修改a

array

不是,例如:

array.pop

在您的测试中,由于您只是修改了对象引用所指向的内容,而不是更改引用,当然您会看到该修改。但是,如果您实际上已将新数组分配给array = ...put something entirely new in `array`... ,那么在调用上下文中这种更改就不会显而易见。

答案 3 :(得分:4)

Ruby,作为Java,是按值传递的...带有一个catch:传递的“值”是(如果是对象)指针引用,所以对象值的任何修改都应该在内部完成方法,它将与调用上下文中的对象相同。

现在,对于你的例子,你需要知道FixNum是只有一个值的“立即”对象 - 在这种情况下,引用不是指针而是对象本身(它仍然是一个对象,使用方法等,所以这不像Java中那样原始。)

在您的示例中,您实际上是将一个新对象重新分配给您的“a”指针,该指针在任何情况下都不会反映在任何地方,方法如下:

my_name = "John"
my_name = "Robert"

实际上正在为引用分配一个新指针。由于无法在Ruby中更改FixNum的值,因此这不是可行的情况。

数组的情况是您可能期望的:您有一个对象,您对对象状态进行了修改并返回修改后的状态。