Ruby 是按引用传递还是按值传递?
@user.update_languages(params[:language][:language1],
params[:language][:language2],
params[:language][:language3])
lang_errors = @user.errors
logger.debug "--------------------LANG_ERRORS----------101-------------"
+ lang_errors.full_messages.inspect
if params[:user]
@user.state = params[:user][:state]
success = success & @user.save
end
logger.debug "--------------------LANG_ERRORS-------------102----------"
+ lang_errors.full_messages.inspect
if lang_errors.full_messages.empty?
@user
对象将错误添加到 update_lanugages
方法中的 lang_errors
变量。 当我对 @user 对象执行保存时,我丢失了最初存储在 lang_errors 变量中的错误。
虽然我试图做的更多的是黑客(这似乎不起作用)。我想了解为什么变量值被冲掉。我理解按引用传递,所以我想知道如何将值保存在该变量中而不被清除。
@user.update_languages(params[:language][:language1],
params[:language][:language2],
params[:language][:language3])
lang_errors = @user.errors
logger.debug "--------------------LANG_ERRORS----------101-------------"
+ lang_errors.full_messages.inspect
if params[:user]
@user.state = params[:user][:state]
success = success & @user.save
end
logger.debug "--------------------LANG_ERRORS-------------102----------"
+ lang_errors.full_messages.inspect
if lang_errors.full_messages.empty?
@user
object adds errors to the lang_errors
variable in the update_lanugages
method.
when I perform a save on the @user
object I lose the errors that were initially stored in the lang_errors
variable.
Though what I am attempting to do would be more of a hack (which does not seem to be working). I would like to understand why the variable values are washed out. I understand pass by reference so I would like to know how the value can be held in that variable without being washed out.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
Ruby 被解释。变量是对数据的引用,但不是数据本身。这有助于对不同类型的数据使用相同的变量。
赋值 lhs = rhs 然后复制 rhs 上的引用,而不是数据。这在其他语言中有所不同,例如 C,其中赋值会将数据从 rhs 复制到 lhs。
因此,对于函数调用,传递的变量(例如 x)确实被复制到函数中的局部变量中,但 x 是一个引用。然后将有两个引用副本,两者都引用相同的数据。一个在调用者中,一个在函数中。
然后,函数中的赋值会将新引用复制到该函数的 x 版本。此后,调用者的 x 版本保持不变。仍然是对原始数据的引用。
相反,在 x 上使用 .replace 方法将导致 ruby 执行数据复制。如果在任何新分配之前使用替换,那么调用者实际上也会看到其版本中的数据更改。
类似地,只要传入变量的原始引用完好无损,实例变量将与调用者看到的相同。在对象的框架内,实例变量始终具有最新的引用值,无论这些引用值是由调用者提供还是在类传递到的函数中设置。
由于对“=”的混淆,“按值调用”或“按引用调用”在这里被混淆了。在编译语言中,“=”是数据副本。在这种解释性语言中,“=”是参考副本。在该示例中,您传递了引用,然后是引用副本,尽管“=”破坏了原始传入的引用,然后人们谈论它,就好像“=”是数据副本一样。
为了与定义保持一致,我们必须保留“.replace”,因为它是数据副本。从'.replace'的角度我们看到这确实是引用传递。此外,如果我们在调试器中浏览,我们会看到传入的引用,因为变量是引用。
但是,如果我们必须保留“=”作为参考框架,那么实际上我们确实可以看到传入的数据,直到赋值为止,然后在赋值之后我们就看不到它了,而调用者的数据保持不变。在行为层面上,只要我们不认为传入的值是复合的,这就是按值传递 - 因为我们无法在单个赋值中更改其他部分时保留其中的一部分(因为该赋值更改参考文献并且原始文献超出范围)。还有一个缺点,就是对象中的实例变量将被引用,就像所有变量一样。因此,我们将被迫谈论传递“按值引用”,并且必须使用相关的措辞。
Ruby is interpreted. Variables are references to data, but not the data itself. This facilitates using the same variable for data of different types.
Assignment of lhs = rhs then copies the reference on the rhs, not the data. This differs in other languages, such as C, where assignment does a data copy to lhs from rhs.
So for the function call, the variable passed, say x, is indeed copied into a local variable in the function, but x is a reference. There will then be two copies of the reference, both referencing the same data. One will be in the caller, one in the function.
Assignment in the function would then copy a new reference to the function's version of x. After this the caller's version of x remains unchanged. It is still a reference to the original data.
In contrast, using the .replace method on x will cause ruby to do a data copy. If replace is used before any new assignments then indeed the caller will see the data change in its version also.
Similarly, as long as the original reference is in tact for the passed in variable, the instance variables will be the same that the caller sees. Within the framework of an object, the instance variables always have the most up to date reference values, whether those are provided by the caller or set in the function the class was passed in to.
The 'call by value' or 'call by reference' is muddled here because of confusion over '=' In compiled languages '=' is a data copy. Here in this interpreted language '=' is a reference copy. In the example you have the reference passed in followed by a reference copy though '=' that clobbers the original passed in reference, and then people talking about it as though '=' were a data copy.
To be consistent with definitions we must keep with '.replace' as it is a data copy. From the perspective of '.replace' we see that this is indeed pass by reference. Furthermore, if we walk through in the debugger, we see references being passed in, as variables are references.
However if we must keep '=' as a frame of reference, then indeed we do get to see the passed in data up until an assignment, and then we don't get to see it anymore after assignment while the caller's data remains unchanged. At a behavioral level this is pass by value as long as we don't consider the passed in value to be composite - as we won't be able to keep part of it while changing the other part in a single assignment (as that assignment changes the reference and the original goes out of scope). There will also be a wart, in that instance variables in objects will be references, as are all variables. Hence we will be forced to talk about passing 'references by value' and have to use related locutions.
是的,但是....
Ruby 传递对对象的引用,并且由于Ruby 中的所有内容都是对象,因此您可以说它是按引用传递。
我不同意这里的帖子声称它是按值传递的,这对我来说似乎是迂腐的、象征性的游戏。
然而,实际上它“隐藏”了行为,因为 ruby 提供了“开箱即用”的大多数操作 - 例如字符串操作,生成对象的副本:
这意味着大多数时候,原始对象保持不变看起来 ruby 是“按值传递”。
当然,在设计自己的类时,了解此行为的细节对于功能行为、内存效率和性能都很重要。
Yes but ....
Ruby passes a reference to an object and since everything in ruby is an object, then you could say it's pass by reference.
I don't agree with the postings here claiming it's pass by value, that seems like pedantic, symantic games to me.
However, in effect it "hides" the behaviour because most of the operations ruby provides "out of the box" - for example string operations, produce a copy of the object:
This means that much of the time, the original object is left unchanged giving the appearance that ruby is "pass by value".
Of course when designing your own classes, an understanding of the details of this behaviour is important for both functional behaviour, memory efficiency and performance.
其他回答者都是正确的,但是一个朋友让我向他解释一下,这实际上归结为 Ruby 如何处理变量,所以我想我会分享一些我为他写的简单图片/解释(对篇幅表示歉意)可能有些过于简单化):
Q1:当您将新变量
str
分配给'foo'
值时会发生什么?A:创建一个名为
str
的标签,指向对象'foo'
,对于这个 Ruby 解释器的状态来说,它恰好位于内存位置2000
。Q2:当使用
=
将现有变量str
分配给新对象时会发生什么?答:标签
str
现在指向不同的对象。Q3:将新变量
=
赋给str
时会发生什么?答:创建一个名为
str2
的新标签,指向 与str
相同的对象。Q4:如果
str
和str2
引用的对象发生变化,会发生什么?A:两个标签仍然指向同一个对象,但该对象本身已发生突变(其内容已更改)成为别的东西)。
这与原来的问题有什么关系?
与Q3/Q4基本相同;该方法获取传递给它的变量/标签 (
str2
) 的私有副本 (str
)。它无法更改标签str
指向哪个对象,但可以更改它们引用的对象的内容以包含 else :The other answerers are all correct, but a friend asked me to explain this to him and what it really boils down to is how Ruby handles variables, so I thought I would share some simple pictures / explanations I wrote for him (apologies for the length and probably some oversimplification):
Q1: What happens when you assign a new variable
str
to a value of'foo'
?A: A label called
str
is created that points at the object'foo'
, which for the state of this Ruby interpreter happens to be at memory location2000
.Q2: What happens when you assign the existing variable
str
to a new object using=
?A: The label
str
now points to a different object.Q3: What happens when you assign a new variable
=
tostr
?A: A new label called
str2
is created that points at the same object asstr
.Q4: What happens if the object referenced by
str
andstr2
gets changed?A: Both labels still point at the same object, but that object itself has mutated (its contents have changed to be something else).
How does this relate to the original question?
It's basically the same as what happens in Q3/Q4; the method gets its own private copy of the variable / label (
str2
) that gets passed in to it (str
). It can't change which object the labelstr
points to, but it can change the contents of the object that they both reference to contain else:在传统术语中,Ruby 是严格按值传递的。然而,Ruby 中的一切都是对象,因此 Ruby 的行为看起来就像按引用传递语言。
Ruby 打破了“按引用传递”或“按值传递”的传统定义,因为一切都是对象,并且当它传递事物时,它会传递对对象的引用。事实上,Ruby 可以被归类为第三种语言,我们可以称之为“通过对象引用传递”。按照计算机科学术语的严格定义,Ruby 是按值传递的。
Ruby 没有任何纯非引用值的概念,因此您不能将值传递给方法。变量始终是对象的引用。为了获得一个不会从你下面改变的对象,你需要复制或克隆你传递的对象,从而给出一个其他人没有引用的对象。然而,即使这样也不是万无一失的:两种标准克隆方法都会进行浅复制,因此克隆的实例变量仍然指向与原始对象相同的对象。如果 ivars 引用的对象发生变化,它仍然会显示在副本中,因为它引用相同的对象。
In traditional terminology, Ruby is strictly pass-by-value. However, everything in Ruby is an object, so Ruby can appear to behave like pass-by-reference languages.
Ruby breaks the traditional definition of "pass-by-reference" or "pass-by-value" because everything is an object, and when it passes things, it passes references to objects. So really, it Ruby can be classified as a 3rd type of language we might call "pass by object reference." In the strict definition of the computer science term, Ruby is pass-by-value.
Ruby doesn't have any concept of a pure, non-reference value, so you can't pass one to a method. Variables are always references to objects. In order to get an object that won't change out from under you, you need to dup or clone the object you're passed, thus giving an object that nobody else has a reference to. However, even this isn't bulletproof: both of the standard cloning methods do a shallow copy, so the instance variables of the clone still point to the same objects that the originals did. If the objects referenced by the ivars mutate, that will still show up in the copy, since it's referencing the same objects.
Ruby 使用“按对象引用传递”
(使用 Python 的术语)。
说 Ruby 使用“按值传递”或“按引用传递”实际上描述性不够,没有什么帮助。我认为现在大多数人都知道,这个术语(“值”与“引用”)来自 C++。
在 C++ 中,“按值传递”意味着函数获取变量的副本,并且对副本的任何更改都不会更改原始变量。对于物体来说也是如此。如果按值传递对象变量,则整个对象(包括其所有成员)都会被复制,并且对成员的任何更改都不会更改原始对象上的这些成员。 (据我所知,如果按值传递指针,情况会有所不同,但 Ruby 没有指针。)
输出:
在 C++ 中,“按引用传递”意味着函数可以访问原始变量。它可以分配一个全新的文字整数,然后原始变量也将具有该值。
输出:
如果参数不是对象,则 Ruby 使用按值传递(在 C++ 意义上)。但在 Ruby 中一切都是对象,因此 Ruby 中实际上不存在 C++ 意义上的值传递。
在 Ruby 中,使用“通过对象引用传递”(使用 Python 的术语):
因此,Ruby 不使用 C++ 意义上的“按引用传递”。如果是这样,那么将新对象分配给函数内的变量将导致旧对象在函数返回后被遗忘。
输出:
* 这就是为什么在 Ruby 中,如果您想要修改函数内的对象,但在函数返回时忘记了这些更改,那么您必须在对副本进行临时更改之前显式创建该对象的副本。
Ruby uses "pass by object reference"
(Using Python's terminology.)
To say Ruby uses "pass by value" or "pass by reference" isn't really descriptive enough to be helpful. I think as most people know it these days, that terminology ("value" vs "reference") comes from C++.
In C++, "pass by value" means the function gets a copy of the variable and any changes to the copy don't change the original. That's true for objects too. If you pass an object variable by value then the whole object (including all of its members) get copied and any changes to the members don't change those members on the original object. (It's different if you pass a pointer by value but Ruby doesn't have pointers anyway, AFAIK.)
Output:
In C++, "pass by reference" means the function gets access to the original variable. It can assign a whole new literal integer and the original variable will then have that value too.
Output:
Ruby uses pass by value (in the C++ sense) if the argument is not an object. But in Ruby everything is an object, so there really is no pass by value in the C++ sense in Ruby.
In Ruby, "pass by object reference" (to use Python's terminology) is used:
Therefore Ruby does not use "pass by reference" in the C++ sense. If it did, then assigning a new object to a variable inside a function would cause the old object to be forgotten after the function returned.
Output:
* This is why, in Ruby, if you want to modify an object inside a function but forget those changes when the function returns, then you must explicitly make a copy of the object before making your temporary changes to the copy.
Ruby 是按值传递的。总是。没有例外。没有如果。没有但是。
这是一个简单的程序,它证明了这一事实:
Ruby is pass-by-value. Always. No exceptions. No ifs. No buts.
Here is a simple program which demonstrates that fact:
Ruby 严格意义上是按值传递,但值是引用。
这可以称为“按值传递引用”。这篇文章有我读过的最好的解释:http ://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/
Pass-reference-by-value可以简单解释如下:
由此产生的行为实际上是按引用传递和按值传递的经典定义的组合。
Ruby is pass-by-value in a strict sense, BUT the values are references.
This could be called "pass-reference-by-value". This article has the best explanation I have read: http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/
Pass-reference-by-value could briefly be explained as follows:
The resulting behavior is actually a combination of the classical definitions of pass-by-reference and pass-by-value.
已经有一些很好的答案,但我想发布关于这个主题的一对权威的定义,但也希望有人能解释一下权威 Matz(Ruby 的创建者)和 David Flanagan 在他们优秀的 O'Reilly 书中的意思, Ruby 编程语言。
直到最后一段,尤其是最后一句话,这一切对我来说都是有意义的。这充其量是一种误导,更糟糕的是令人困惑。以任何方式,对按值传递引用的修改如何改变底层对象?
There are already some great answers, but I want to post the definition of a pair of authorities on the subject, but also hoping someone might explain what said authorities Matz (creator of Ruby) and David Flanagan meant in their excellent O'Reilly book, The Ruby Programming Language.
This all makes sense to me until that last paragraph, and especially that last sentence. This is at best misleading, and at worse confounding. How, in any way, could modifications to that passed-by-value reference change the underlying object?
Ruby 是按引用传递的。总是。没有例外。没有如果。没有但是。
这是一个简单的程序,它证明了这一事实:
Ruby is pass-by-reference. Always. No exceptions. No ifs. No buts.
Here is a simple program which demonstrates that fact:
参数是原始参考的副本。因此,您可以更改值,但无法更改原始参考。
Parameters are a copy of the original reference. So, you can change values, but cannot change the original reference.
试试这个:--
标识符 a 包含值对象 1 的 object_id 3,标识符 b 包含值对象 2 的 object_id 5。
现在这样做:--
现在,a 和 b 都包含相同的 object_id 5,它引用值对象 2。
因此,Ruby 变量包含 object_ids 来引用值对象。
执行以下操作也会出现错误:--
但这样做不会出现错误:--
这里标识符 a 返回值对象 11,其对象 id 为 23,即 object_id 23 位于标识符 a 处,现在我们看一个使用方法的示例。
foo 中的 arg 被赋予返回值 x。
它清楚地表明参数是通过值 11 传递的,而值 11 本身就是一个对象,具有唯一的对象 id 23。
现在还可以看到这一点:--
此处,标识符 arg 首先包含 object_id 23 来引用 11,然后使用值对象 12 进行内部赋值,它包含 object_id 25。但它不会更改调用方法中使用的标识符 x 引用的值。
因此,Ruby 是按值传递的,Ruby 变量不包含值,但包含对值对象的引用。
Try this:--
identifier a contains object_id 3 for value object 1 and identifier b contains object_id 5 for value object 2.
Now do this:--
Now, a and b both contain same object_id 5 which refers to value object 2.
So, Ruby variable contains object_ids to refer to value objects.
Doing following also gives error:--
but doing this won't give error:--
Here identifier a returns value object 11 whose object id is 23 i.e. object_id 23 is at identifier a, Now we see an example by using method.
arg in foo is assigned with return value of x.
It clearly shows that argument is passed by value 11, and value 11 being itself an object has unique object id 23.
Now see this also:--
Here, identifier arg first contains object_id 23 to refer 11 and after internal assignment with value object 12, it contains object_id 25. But it does not change value referenced by identifier x used in calling method.
Hence, Ruby is pass by value and Ruby variables do not contain values but do contain reference to value object.
应该注意的是,您甚至不必使用“替换”方法来更改值的原始值。如果您为哈希分配其中一个哈希值,则您将更改原始值。
It should be noted that you do not have to even use the "replace" method to change the value original value. If you assign one of the hash values for a hash, you are changing the original value.
同一对象中的任何更新都不会引用新内存,因为它仍然位于同一内存中。
以下是一些例子:
Any updates in the same object won't make the references to new memory since it still is in same memory.
Here are few examples :
许多精彩的答案都深入探讨了 Ruby 的“按值传递引用” 的工作原理。但通过实例我可以更好地学习和理解一切。希望这会有所帮助。
正如您所看到的,当我们进入该方法时,我们的栏仍然指向字符串“value”。但随后我们将一个字符串对象“reference”分配给bar,它有一个新的object_id。在这种情况下,foo 内部的 bar 具有不同的范围,并且我们在方法内部传递的任何内容都不再被 bar 访问,因为我们重新分配它并将其指向内存中保存字符串“引用”的新位置。
现在考虑同样的方法。唯一的区别是方法内部的 do
注意到区别了吗?我们在这里所做的是:我们修改了该变量所指向的 String 对象的内容。方法内部 bar 的范围仍然不同。
因此,要小心如何对待传递到方法中的变量。如果您就地修改传入的变量(gsub!、replace 等),请在方法名称中用感叹号 ! 进行指示,例如“def foo!”
PS:
重要的是要记住,foo 内部和外部的“bar”是“不同的”“bar”。它们的范围不同。在该方法内部,您可以将“bar”重命名为“club”,结果是相同的。
我经常看到变量在方法内部和外部重复使用,虽然这很好,但它降低了代码的可读性,并且是一种代码味道(恕我直言)。我强烈建议不要做我在上面的例子中所做的事情:)而是这样做
Lots of great answers diving into the theory of how Ruby's "pass-reference-by-value" works. But I learn and understand everything much better by example. Hopefully, this will be helpful.
As you can see when we entered the method, our bar was still pointing to the string "value". But then we assigned a string object "reference" to bar, which has a new object_id. In this case bar inside of foo, has a different scope, and whatever we passed inside the method, is no longer accessed by bar as we re-assigned it and point it to a new place in memory that holds String "reference".
Now consider this same method. The only difference is what with do inside the method
Notice the difference? What we did here was: we modified the contents of the String object, that variable was pointing to. The scope of bar is still different inside of the method.
So be careful how you treat the variable passed into methods. And if you modify passed-in variables-in-place (gsub!, replace, etc), then indicate so in the name of the method with a bang !, like so "def foo!"
P.S.:
It's important to keep in mind that the "bar"s inside and outside of foo, are "different" "bar". Their scope is different. Inside the method, you could rename "bar" to "club" and the result would be the same.
I often see variables re-used inside and outside of methods, and while it's fine, it takes away from the readability of the code and is a code smell IMHO. I highly recommend not to do what I did in my example above :) and rather do this