Ruby 是按引用传递还是按值传递?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1872110/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 22:01:50  来源:igfitidea点击:

Is Ruby pass by reference or by value?

ruby-on-railsrubypass-by-reference

提问by Sid

@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?

@userobject adds errors to the lang_errorsvariable in the update_lanugagesmethod. when I perform a save on the @userobject I lose the errors that were initially stored in the lang_errorsvariable.

@user对象lang_errorsupdate_lanugages方法中的变量添加错误。当我对@user对象执行保存时,我丢失了最初存储在lang_errors变量中的错误。

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.

虽然我试图做的更像是一种黑客攻击(这似乎不起作用)。我想了解为什么变量值被洗掉了。我理解通过引用传递,所以我想知道如何将值保存在该变量中而不会被清除。

采纳答案by Chuck

In traditional terminology, Ruby is strictly pass-by-value. But that's not really what you're asking here.

在传统术语中,Ruby 是严格按值传递的。但这并不是你在这里要问的。

Ruby doesn't have any concept of a pure, non-reference value, so you certainly 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. (Even this isn't bulletproof, though — 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 没有任何纯非引用值的概念,因此您当然不能将一个值传递给方法。变量总是对对象的引用。为了获得一个不会从你身下改变的对象,你需要复制或克隆你传递的对象,从而提供一个其他人无法引用的对象。(即使这也不是万无一失的——两种标准克隆方法都进行浅拷贝,因此克隆的实例变量仍然指向与原始对象相同的对象。如果 ivars 引用的对象发生变异,那将仍然出现在副本中,因为它引用了相同的对象。)

回答by Abe Voelker

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):

其他回答者都是正确的,但一个朋友让我向他解释这一点,归根结底是Ruby如何处理变量,所以我想我会分享一些我为他写的简单图片/解释(抱歉篇幅过长并且可能有些过于简单化):



Q1: What happens when you assign a new variable strto a value of 'foo'?

Q1:当你将一个新的变量会发生什么str来的价值'foo'

str = 'foo'
str.object_id # => 2000

enter image description here

在此处输入图片说明

A: A label called stris created that points at the object 'foo', which for the state of this Ruby interpreter happens to be at memory location 2000.

答:str创建了一个名为 的标签,它指向对象'foo',对于这个 Ruby 解释器的状态,它恰好位于内存位置2000



Q2: What happens when you assign the existing variable strto a new object using =?

Q2:当您使用 将现有变量分配给str新对象时会发生什么=

str = 'bar'.tap{|b| puts "bar: #{b.object_id}"} # bar: 2002
str.object_id # => 2002

enter image description here

在此处输入图片说明

A: The label strnow points to a different object.

A:标签str现在指向不同的对象。



Q3: What happens when you assign a new variable =to str?

Q3:当你将一个新的变量会发生什么=str

str2 = str
str2.object_id # => 2002

enter image description here

在此处输入图片说明

A: A new label called str2is created that points at the same objectas str.

答:所谓的新标签str2被创建在点相同的对象str



Q4: What happens if the object referenced by strand str2gets changed?

Q4:如果引用str和的对象发生了str2变化,会发生什么?

str2.replace 'baz'
str2 # => 'baz'
str  # => 'baz'
str.object_id # => 2002
str2.object_id # => 2002

enter image description here

在此处输入图片说明

A: Both labels still point at the same object, but that object itself has mutated (its contents have changed to be something else).

A:两个标签仍然指向同一个对象,但该对象本身发生了变异(其内容已更改为其他内容)。



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 label strpoints to, but it can change the contents ofthe object that they both reference to contain else:

与第三季度/第四季度发生的情况基本相同;该方法获取str2传递给它的变量/标签 ( )的私有副本( str)。它不能更改标签str指向的对象,但可以更改它们都引用的对象的内容,以包含 else:

str = 'foo'

def mutate(str2)
  puts "str2: #{str2.object_id}"
  str2.replace 'bar'
  str2 = 'baz'
  puts "str2: #{str2.object_id}"
end

str.object_id # => 2004
mutate(str) # str2: 2004, str2: 2006
str # => "bar"
str.object_id # => 2004

回答by David Winiecki

Ruby uses "pass by object reference"

Ruby 使用“通过对象引用传递”

(Using Python's terminology.)

(使用 Python 的术语。)

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++.

说 Ruby 使用“按值传递”或“按引用传递”并不足以描述到有用的地方。我认为正如现在大多数人所知道的那样,术语(“值”与“引用”)来自 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.)

在 C++ 中,“按值传递”意味着函数获取变量的副本,并且对副本的任何更改都不会更改原始变量。对于对象也是如此。如果按值传递对象变量,则整个对象(包括其所有成员)都会被复制,并且对成员的任何更改都不会更改原始对象上的这些成员。(如果您按值传递指针则不同,但 Ruby 无论如何都没有指针,AFAIK。)

class A {
  public:
    int x;
};

void inc(A arg) {
  arg.x++;
  printf("in inc: %d\n", arg.x); // => 6
}

void inc(A* arg) {
  arg->x++;
  printf("in inc: %d\n", arg->x); // => 1
}

int main() {
  A a;
  a.x = 5;
  inc(a);
  printf("in main: %d\n", a.x); // => 5

  A* b = new A;
  b->x = 0;
  inc(b);
  printf("in main: %d\n", b->x); // => 1

  return 0;
}

Output:

输出:

in inc: 6
in main: 5
in inc: 1
in main: 1

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.

在 C++ 中,“通过引用传递”意味着函数可以访问原始变量。它可以分配一个全新的文字整数,然后原始变量也将具有该值。

void replace(A &arg) {
  A newA;
  newA.x = 10;
  arg = newA;
  printf("in replace: %d\n", arg.x);
}

int main() {
  A a;
  a.x = 5;
  replace(a);
  printf("in main: %d\n", a.x);

  return 0;
}

Output:

输出:

in replace: 10
in main: 10

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.

如果参数不是对象,Ruby 使用按值传递(在 C++ 意义上)。但是在 Ruby 中,一切都是对象,因此在 Ruby 中,C++ 意义上确实没有值传递。

In Ruby, "pass by object reference" (to use Python's terminology) is used:

在 Ruby 中,使用“通过对象引用传递”(使用 Python 的术语):

  • Inside the function, any of the object's members can have new values assigned to them and these changes will persist after the function returns.*
  • Inside the function, assigning a whole new object to the variable causes the variable to stop referencing the old object. But after the function returns, the original variable will still reference the old object.
  • 在函数内部,对象的任何成员都可以分配新值,并且这些更改将在函数返回后持续存在。*
  • 在函数内部,将一个全新的对象分配给变量会导致变量停止引用旧对象。但函数返回后,原变量仍会引用旧对象。

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.

因此,Ruby 不使用 C++ 意义上的“通过引用传递”。如果是这样,那么将新对象分配给函数内的变量会导致在函数返回后忘记旧对象。

class A
  attr_accessor :x
end

def inc(arg)
  arg.x += 1
  puts arg.x
end

def replace(arg)
  arg = A.new
  arg.x = 3
  puts arg.x
end

a = A.new
a.x = 1
puts a.x  # 1

inc a     # 2
puts a.x  # 2

replace a # 3
puts a.x  # 2

puts ''

def inc_var(arg)
  arg += 1
  puts arg
end

b = 1     # Even integers are objects in Ruby
puts b    # 1
inc_var b # 2
puts b    # 1

Output:

输出:

1
2
2
3
2

1
2
1

* 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 中,如果您想修改函数内部的对象,但在函数返回时忘记这些更改,那么您必须在对副本进行临时更改之前显式地制作该对象的副本。

回答by J?rg W Mittag

Is Ruby pass by reference or by value?

Ruby 是按引用传递还是按值传递?

Ruby is pass-by-value. Always. No exceptions. No ifs. No buts.

Ruby 是按值传递的。总是。没有例外。没有如果。没有但是。

Here is a simple program which demonstrates that fact:

这是一个简单的程序,它证明了这一事实:

def foo(bar)
  bar = 'reference'
end

baz = 'value'

foo(baz)

puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value

回答by Ari

Ruby is pass-by-value in a strict sense, BUT the values are references.

Ruby 是严格意义上的值传递,但值是引用。

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/

这可以称为“按值传递引用”。这篇文章有我读过的最好的解释: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:

按值传递引用可以简要解释如下:

A function receives a reference to (and will access) the same object in memory as used by the caller. However, it does not receive the box that the caller is storing this object in; as in pass-value-by-value, the function provides its own box and creates a new variable for itself.

函数接收对(并将访问)与调用者使用的内存中相同对象的引用。但是,它不会接收调用者存储此对象的框;与按值传递值一样,该函数提供自己的框并为自己创建一个新变量。

The resulting behavior is actually a combination of the classical definitions of pass-by-reference and pass-by-value.

结果行为实际上是按引用传递和按值传递的经典定义的组合。

回答by Dominick

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.

已经有一些很好的答案,但我想发布关于该主题的一对权威的定义,但也希望有人能解释权威 Matz(Ruby 的创造者)和 David Flanagan 在他们出色的 O'Reilly 书中所说的意思,Ruby 编程语言

[from 3.8.1: Object References]

When you pass an object to a method in Ruby, it is an object reference that is passed to the method. It is not the object itself, and it is not a reference to the reference to the object. Another way to say this is that method arguments are passed by valuerather than by reference, but that the values passed are object references.

Because object references are passed to methods, methods can use those references to modify the underlying object. These modifications are then visible when the method returns.

[来自 3.8.1:对象引用]

当您将对象传递给 Ruby 中的方法时,它是传递给该方法的对象引用。它不是对象本身,也不是对对象的引用的引用。另一种说法是方法参数通过而不是通过引用传递,但传递的值是对象引用。

因为对象引用被传递给方法,所以方法可以使用这些引用来修改底层对象。当方法返回时,这些修改是可见的。

This all makes sense to me until that last paragraph, and especiallythat 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?

直到最后一段,尤其是最后一句话,这一切对我来说都是有意义的。这充其量是误导,更糟糕的是混淆。无论如何,对按值传递引用的修改如何改变底层对象?

回答by Brett Allred

Is Ruby pass by reference or by value?

Ruby 是按引用传递还是按值传递?

Ruby is pass-by-reference. Always. No exceptions. No ifs. No buts.

Ruby 是按引用传递的。总是。没有例外。没有如果。没有但是。

Here is a simple program which demonstrates that fact:

这是一个简单的程序,它证明了这一事实:

def foo(bar)
  bar.object_id
end

baz = 'value'

puts "#{baz.object_id} Ruby is pass-by-reference #{foo(baz)} because object_id's (memory addresses) are always the same ;)"

=> 2279146940 Ruby is pass-by-reference 2279146940 because object_id's (memory addresses) are always the same ;)

=> 2279146940 Ruby 是传递引用 2279146940 因为 object_id(内存地址)总是相同的;)

def bar(babar)
  babar.replace("reference")
end

bar(baz)

puts "some people don't realize it's reference because local assignment can take precedence, but it's clearly pass-by-#{baz}"

=> some people don't realize it's reference because local assignment can take precedence, but it's clearly pass-by-reference

=> 有些人没有意识到它是引用,因为本地赋值可以优先,但它显然是按引用传递的

回答by Rael Gugelmin Cunha

Parameters are a copy of the original reference. So, you can change values, but cannot change the original reference.

参数是原始引用的副本。因此,您可以更改值,但不能更改原始引用。

回答by Alok Anand

Try this:--

尝试这个: -

1.object_id
#=> 3

2.object_id
#=> 5

a = 1
#=> 1
a.object_id
#=> 3

b = 2
#=> 2
b.object_id
#=> 5

identifier a contains object_id 3 for value object 1 and identifier b contains object_id 5 for value object 2.

标识符 a 包含值对象 1 的 object_id 3,标识符 b 包含值对象 2 的 object_id 5。

Now do this:--

现在这样做:--

a.object_id = 5
#=> error

a = b
#value(object_id) at b copies itself as value(object_id) at a. value object 2 has object_id 5
#=> 2

a.object_id 
#=> 5

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.

现在,a 和 b 都包含引用值对象 2 的相同 object_id 5。因此,Ruby 变量包含 object_ids 来引用值对象。

Doing following also gives error:--

执行以下操作也会出错:--

c
#=> error

but doing this won't give error:--

但这样做不会出错:--

5.object_id
#=> 11

c = 5
#=> value object 5 provides return type for variable c and saves 5.object_id i.e. 11 at c
#=> 5
c.object_id
#=> 11 

a = c.object_id
#=> object_id of c as a value object changes value at a
#=> 11
11.object_id
#=> 23
a.object_id == 11.object_id
#=> true

a
#=> Value at a
#=> 11

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.

这里标识符 a 返回值对象 11,其对象 id 为 23,即 object_id 23 位于标识符 a,现在我们看一个使用方法的示例。

def foo(arg)
  p arg
  p arg.object_id
end
#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23

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.

foo 中的 arg 被赋值为 x 的返回值。它清楚地表明参数通过值 11 传递,值 11 本身是一个对象,具有唯一的对象 ID 23。

Now see this also:--

现在也看到这个:--

def foo(arg)
  p arg
  p arg.object_id
  arg = 12
  p arg
  p arg.object_id
end

#=> nil
11.object_id
#=> 23
x = 11
#=> 11
x.object_id
#=> 23
foo(x)
#=> 11
#=> 23
#=> 12
#=> 25
x
#=> 11
x.object_id
#=> 23

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.

这里,标识符 arg 首先包含 object_id 23 来引用 11,在内部赋值给值对象 12 之后,它包含 object_id 25。但它不会改变调用方法中使用的标识符 x 引用的值。

Hence, Ruby is pass by value and Ruby variables do not contain values but do contain reference to value object.

因此,Ruby 是按值传递的,Ruby 变量不包含值但包含对值对象的引用。

回答by Don Carr

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.

需要注意的是,您甚至不必使用“替换”方法来更改值的原始值。如果您为散列分配散列值之一,则您正在更改原始值。

def my_foo(a_hash)
  a_hash["test"]="reference"
end;

hash = {"test"=>"value"}
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"