Ruby 是按值传递还是按引用传递?[复制]

Is Ruby pass-by-value or pass-by-reference? [duplicate]

提问人:Arun 提问时间:5/9/2013 最后编辑:vadasambarArun 更新时间:10/23/2017 访问量:5456

问:

我基本上是一个 java 开发人员。我在 ruby 领域工作了大约一年。与 Java 不同,Ruby 是一种纯粹的面向对象的编程语言。这里有一个疑问。是按值传递还是按引用传递?Java 充当按值传递:“传递基元时,我看到值被复制并传递给方法。但是对于 Objects,引用将被复制并传递给方法。引用包含对象在堆中的位置。在方法调用期间,仅传递对象的位置。因此,不会创建重复的对象。修改了相同的对象。

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

结果:

A 内部 甲基1---10

AA-----5

Meth2---34 内部数组

arr---34

Java Ruby OOP 按引用传递

评论

0赞 phuclv 6/1/2014
在此处此处复制

答:

6赞 Arup Rakshit 5/9/2013 #1

见下文,将回答您的所有问题: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

6赞 T.J. Crowder 5/9/2013 #2

在这两种情况下,它都是按值传递的,就像 Java 一样。不同之处在于,测试中的两个项目都是对象,而在 Java 中,一个是基元,另一个是对象。但是,某物是基元还是对象与按值传递与按引用传递无关。按值传递与按引用传递与被调用方法可以对传递给它的调用上下文中的变量执行的操作有关。

让我们忽略语言和对象,只看一下按值传递与按引用传递的实际含义。我将以模糊的 B/Java/C/C++/C#/D 语法使用伪代码:

Function Foo(arg) {
  arg = 6
}

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

如果按值传递,则输出为 5。如果通过引用传递(对变量的引用给出),则输出为 6,因为通过对变量的引用进行处理。aaaFooFooa

请注意,两个测试之间存在实质性差异。

在第一个测试中,您将为以下内容分配一个全新的值:a

a = a + 5

您不是在修改传入方法的版本,而是使用该值为 .aa

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

array.pop

例如,不是:

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

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

评论

0赞 newacct 5/10/2013
“测试中的两个项目都是对象”实际上它们都是引用。“对象”不是 Java 或 Ruby 中的值
0赞 T.J. Crowder 5/10/2013
@newacct:不,测试中的两个项目都是对象(数字对象和数组对象)。是的,当这些被传递到函数中时,传递的是对该对象(而不是对象本身)的引用,从上面可以清楚地看出这一点。测试是整个事情,而不仅仅是将对象引用传递到函数中的窄位。
0赞 newacct 5/11/2013
@T.J.Crowder:不,不是。“传球”没有什么特别的。语言中 ANY 表达式的值是引用。将表达式分配给变量时,就是在分配引用。
0赞 T.J. Crowder 5/11/2013
@newacct:你分配给变量的东西不是事物“是什么”。该测试使用对象,我们通过引用来引用这些对象。
1赞 newacct 5/11/2013
@T.J.Crowder:你说“一个是原始的,另一个是对象”,好像“原始”和“对象”是可比的东西。但原语是 Java 中的值。对象不是 Java 中的值。你让人们感到困惑,以为有些东西要么是原始的,要么是对象,但事实并非如此。Java 中的值要么是原语,要么是引用。“对象”只是引用可以指向的东西。
4赞 Martin 5/9/2013 #3

Ruby 和 Java 一样,是按值传递的......使用 catch :传递的“值”是(如果是对象)指针引用,因此如果在方法内部对对象值进行任何修改,它将与调用上下文中的对象位于同一对象上。

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

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

my_name = "John"
my_name = "Robert"

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

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

19赞 Jörg W Mittag 5/9/2013 #4

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

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

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

下面是一个简单的示例,您可以运行该示例来确定 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赞 Arup Rakshit 5/9/2013
on all existing Ruby implementations Symbols, Fixnums and Floats are actually passed directly by value and not with an intermediary pointer.我非常喜欢,我想,会包括。但你做到了,似乎很好。:)但我认为你也应该包括 ,也包括在你的列表中。truenilfalse
0赞 Mr Mikkél 2/12/2015
要进一步将其带回家,请在 foo = 'No....说“foo.relpace('some text')”