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

Posted

技术标签:

【中文标题】Ruby 是按值传递还是按引用传递? [复制]【英文标题】:Is Ruby pass-by-value or pass-by-reference? [duplicate] 【发布时间】:2013-05-03 16:03:15 【问题描述】:

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

但是当我尝试下面的 ruby​​ 代码 sn-p 时,得到的结果与在 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

【问题讨论】:

重复 here 和 here 【参考方案1】:

Ruby 使用按值传递,或者更准确地说,是按值传递的一种特殊情况,其中传递的值始终 是一个指针。这种特殊情况有时也称为 call-by-sharing、call-by-object-sharing 或 call-by-object。

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

注意:在所有现有的 Ruby 实现中,Symbols、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!'

【讨论】:

on all existing Ruby implementations Symbols, Fixnums and Floats are actually passed directly by value and not with an intermediary pointer. 我非常喜欢,我想,会包括。但你做到了,看起来不错。 :) 但我认为您还应该在您的列表中包括truenilfalse 为了进一步推动它回家,在 foo = 'No.... 之后添加一行,说“foo.relpace('some text')”【参考方案2】:

见下文,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 到它被更改的方法。

【讨论】:

太棒了!!!你是个天才。感谢您的详细回复:)【参考方案3】:

在这两种情况下都是按值传递,例如 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 分配了一个新数组,则该更改在调用上下文中不会很明显。

【讨论】:

感谢您的回答:) "你的测试中的两个项目都是对象" 实际上它们都是引用。 “对象”不是 Java 或 Ruby 中的值 @newacct:不,测试中的两个项目都是对象(数字对象和数组对象)。是的,当它们被传递给函数时,传递的是对该对象的引用(而不是对象本身),从上面可以清楚地看出。测试是整个事情,而不仅仅是将对象引用传递给函数的那一小部分。 @T.J.Crowder:不,不是。 “通过”并没有什么特别之处。语言中任何表达式的值都是参考。当您将表达式分配给变量时,您正在分配一个引用。 @T.J.Crowder:你说“一个是原始的,另一个是对象”,好像“原始”和“对象”是可比较的东西。但是原语是 Java 中的值。对象不是 Java 中的值。你让人们误以为有些东西要么是原始的,要么是对象的,但实际上并不存在。 Java 中的值要么是原始值,要么是引用。 “对象”只是引用可以指向的东西。【参考方案4】:

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

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

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

my_name = "John"
my_name = "Robert"

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

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

【讨论】:

以上是关于Ruby 是按值传递还是按引用传递? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

java中的参数传递是按引用传递还是按值传递

JavaScript 是按引用传递还是按值传递语言?

js传参是按值传递还是按引用传递?

.bind(this) 是按引用传递还是按值传递?

PHP 变量是按值传递还是按引用传递?

JS是按值传递还是按引用传递