4

我基本上是一个java开发人员。我在红宝石工作了大约一年。与 java 不同,Ruby 是一种纯面向对象的编程语言。这里有一个疑问。是传值还是传引用?Java 作为按值传递工作:“在传递原语时,我看到值被复制并传递给方法。但在对象的情况下,引用被复制并传递给方法。引用包含对象的位置在堆中。在方法调用过程中,只传递了对象的位置。因此不会创建重复的对象。修改相同的对象。

但是当我尝试下面的 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}"

结果:

内甲1---10

啊-----5

meth2内的数组---34

arr---34

4

4 回答 4

19

Ruby 使用按值传递,或者更准确地说,是按值传递的一种特殊情况,其中传递的值始终是一个指针。这种特殊情况有时也称为共享调用、对象共享调用或对象调用。

Java(用于对象)、C#(默认情况下用于引用类型)、Smalltalk、Python、ECMAScript/JavaScript 以及几乎所有创建的面向对象语言都使用相同的约定。

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

这是一个简单的示例,您可以运行它来确定 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!'
于 2013-05-09T14:48:09.363 回答
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在本地对它被更改的方法进行引用。

于 2013-05-09T08:22:28.983 回答
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,而是使用该值将值分配给a.

在您的第二个测试中,您只是在修改array

array.pop

不是,例如:

array = ...put something entirely new in `array`...

在您的测试中,由于您只是在修改对象引用指向的东西,而不是更改引用,因此您当然会看到该修改。但是,如果您实际上为 分配了一个新数组array,则该更改在调用上下文中不会很明显。

于 2013-05-09T08:22:41.390 回答
4

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

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

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

my_name = "John"
my_name = "Robert"

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

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

于 2013-05-09T08:24:39.677 回答