Ruby’按值传递’澄清

好的,所以Ruby是’按值传递’。 但是,如何在Ruby中精确定义“按引用传递”和“按值传递”? 我用过这个答案传递参考值和传递值之间有什么区别? 根据它,Ruby似乎是一个混合……

从技术上讲,Ruby似乎是“按值传递”,区别在于当您将值传递给方法时该值不会获得COPIED。 如果我们定义“value”=对象,并且“reference”=指向该对象的引用变量,那么“通过引用传递”是否有意义,如果它等同于“传递指向特定对象的引用变量”? 然后,一旦传递了“引用”,该方法就不会对对象进行COPY,而实际上具有可以直接操作的ORIGINAL对象本身(由变量引用)。 如我错了请纠正我。

编辑:我知道这个问题Ruby是通过引用还是通过值传递? 但不同的人似乎对那里的参考/价值有不同的定义。

Ruby是按值传递的,就像C,Java,Python,Smalltalk,ECMAScript等等。 默认情况下,C ++和C#也是值传递,你必须使用特殊注释(在C ++中,在C#中ref ),以使用传递引用。

区别实际上相当简单:如果传递了引用,则被调用者可以修改它,否则它不能。 在Ruby中,被调用者无法修改引用,因为它是按值传递:

 def is_ruby_pass_by_value?(foo) 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 # 'Yes, of course, Ruby *is* pass-by-value!' 

如您所见,在方法is_ruby_pass_by_value? ,参考bar / foo 未被传递,否则之后可以看到修改。 bar正在按值传递,即bar内容 (包含在其中的 )正在传递,而不是引用本身。

现在,传递的价值是多少? 它不是 String对象。 而是它是指向 String对象的指针 。 更准确地说:该指针的副本

现在,有两个指向String对象的指针。 那个String对象是可变的! 所以,如果我按照一个指针( foo )并告诉String对象改变它自己,然后我按照另一个指针( bar )询问它的内容,那么我显然会看到更改的内容。 这只是共享可变状态的本质,Ruby不是纯粹的function,引用透明的语言:

 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!' 

实际上,在Ruby中,变量保存并作为参数传递的值始终是指针。 这就是几乎所有面向对象语言的工作方式。 Barbara Liskov将这种特殊情况称为“按对象分享呼叫”,它有时也被称为“按共享呼叫”或“逐个呼叫”。

但请注意,传递的值是指针的事实完全无关紧要。 按值传递与按引用传递是关于如何传递参数,而不是参数是什么 。 无论是传递int还是指针,C总是按值传递。 指针仍然被价值所传递。 同样在Ruby中,指针正在按值传递。 Ruby和C之间的区别是a)你只能在Ruby中传递指针,而b)没有特殊的语法表明你正在传递一个指针。

[注意:大多数Ruby实现实际上都会进行优化,以便传递直接小于指针的对象,而不是将指针传递给该对象。 但是,它们只针对语言规范保证的对象非常不可变,因此无法观察将指针传递给值并直接传递值之间的区别。 例如,对于FixnumSymbol s, Float s, niltruefalsetrue 。]

下面是C#中的一个示例,它演示了pass-by-value(即使该值是引用)和pass-by-reference之间的区别:

 class Program { static void IsCSharpPassByValue(string[] foo, ref string baz) { foo[0] = "More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value."; foo = new string[] { "C# is not pass-by-reference." }; baz = "It also supports pass-by-reference if explicitly requested."; } static void Main(string[] args) { var quux = new string[] { "Yes, of course, C# *is* pass-by-value!" }; var grault = "This string will vanish because of pass-by-reference."; IsCSharpPassByValue(quux, ref grault); Console.WriteLine(quux[0]); // More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value. Console.WriteLine(grault); // It also supports pass-by-reference if explicitly requested. } } 

你可以对此进行纯粹主义,并说ruby是一个“通过参考值传递”的特例,但它忽略了这一点。 想想ruby中的一切都是一个对象

foo(14)传递对值为14的整数对象的引用。有些东西在封面下进行,所以你不会得到100个14个对象,但是从意图的角度来看大部分都是时间你可以忘记这个概念。

Ruby是“通过引用传递”。 区别如下:如果通过引用传递,则可以对原始对象执行错误操作:

 x = [ "virgin" ] def do_bad_things_to( arg ) arg.clear << "bad things" end do_bad_things_to( x ) 

如果您通过值传递,您将获得原始对象的值并可以使用它,但您不能对原始对象执行错误操作。 您消耗更多内存,因为原始对象值的副本也占用内存:

 def pass_by_value( arg ) arg.dup end y = [ "virgin" ] do_bad_things_to( pass_by_value( y ) ) px #=> [ "bad things" ] py #=> [ "virgin" ] 

对于不可变对象(数字,符号, truefalsenil ...),人们不能凭借其不变性来做坏事。 据说,在Ruby中,它们是按值传递的,但事实上,这种区别对它们来说没什么意义,就像在内存中保留其内部的许多副本一样没有意义。

更新:关于“参考”的含义似乎存在术语争用。 在Ruby中,JörgMittag的“按引用传递”是通过关闭局部变量的闭包明确实现的:

 baz = "Jörg" define_method :pass_by_Jorgs_reference_to_baz do baz = "Boris" end pass_by_Jorgs_reference_to_baz baz #=> "Boris"