Ruby是按值传递还是按引用传递?
我基本上是一个java开发人员。 我在ruby工作了大约一年。 与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
Ruby使用pass-by-value,或者更确切地说,是一个传递值的特殊情况,其中传递的值总是一个指针。 这种特殊情况有时也称为按共享呼叫,按对象分配或按对象调用。
它与Java(用于对象),C#(默认情况下用于引用类型),Smalltalk,Python,ECMAScript / JavaScript以及或多或少的所有面向对象语言所使用的约定相同。
注意:在所有现有的Ruby实现中, Symbol
, Fixnum
和Float
实际上是通过值直接传递的,而不是通过中间指针传递的。 但是,由于这三个是不可变的,在这种情况下,传值和对象共享之间没有可观察到的行为差异,因此您可以通过简单地将所有内容视为逐个对象来大大简化您的心理模型-sharing。 只需将这三种特殊情况解释为内部编译器优化,您无需担心。
这是一个简单的例子,您可以运行以确定传递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!'
它在两种情况下都是传值,比如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 + 5
您没有修改传递给方法的版本,您正在使用该值为a分配新值。
在第二次测试中,您只是修改array
:
array.pop
不是,例如:
array = ...put something entirely new in `array`...
在您的测试中,由于您只是修改了对象引用所指向的内容,而不是更改引用,当然您会看到该修改。 但是,如果您实际上已将新数组分配给array
,那么在调用上下文中这种更改就不会很明显。
请参阅下文, 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
的方法。
Ruby,作为Java,是按值传递的…带有一个catch:传递的“值”是(在对象的情况下)指针引用,因此对象值的任何修改都应该在方法内完成,它将与调用上下文中的对象位于同一对象上。
现在,在你的例子中,你需要知道FixNum是只有一个值的“立即”对象 – 在这种情况下,引用不是指针而是对象本身(它仍然是一个对象,有方法等,所以这个不像Java那样原始)。
在您的示例中,您实际上是将一个新对象重新分配给“a”指针,该指针在任何情况下都不会在任何地方反映,方法如下:
my_name = "John" my_name = "Robert"
实际上是为引用分配一个新指针。 由于无法在Ruby中更改FixNum的值,因此这不是可行的情况。
数组的情况是您可能期望的:您有一个对象,您对对象状态进行修改并返回修改后的状态。