Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails Ruby是按引用传递还是按值传递?_Ruby On Rails_Ruby_Pass By Reference - Fatal编程技术网

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

Ruby on rails Ruby是按引用传递还是按值传递?,ruby-on-rails,ruby,pass-by-reference,Ruby On Rails,Ruby,Pass By Reference,@user对象将错误添加到update\u lanugages方法中的lang\u errors变量中。 当我对@user对象执行保存时,我丢失了最初存储在langu errors变量中的错误 虽然我试图做的更多的是一个黑客(这似乎不起作用)。我想了解为什么变量值被洗掉了。我理解通过引用传递,因此我想知道如何将值保存在该变量中而不被冲掉。在传统术语中。但这不是你要问的 Ruby没有任何纯的、非引用值的概念,因此您当然不能将其传递给方法。变量始终是对对象的引用。为了得到一个不会从你下面变出来的对象

@user
对象将错误添加到
update\u lanugages
方法中的
lang\u errors
变量中。 当我对
@user
对象执行保存时,我丢失了最初存储在
langu errors
变量中的错误


虽然我试图做的更多的是一个黑客(这似乎不起作用)。我想了解为什么变量值被洗掉了。我理解通过引用传递,因此我想知道如何将值保存在该变量中而不被冲掉。

在传统术语中。但这不是你要问的

Ruby没有任何纯的、非引用值的概念,因此您当然不能将其传递给方法。变量始终是对对象的引用。为了得到一个不会从你下面变出来的对象,你需要复制或克隆你所传递的对象,从而得到一个其他人都没有引用的对象。(尽管如此,这也不是绝对可靠的——两种标准克隆方法都是浅拷贝,因此克隆的实例变量仍然指向与原始对象相同的对象。如果IVAR引用的对象发生变异,这仍然会显示在副本中,因为它引用的是相同的对象。)

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

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

其他回答都是正确的,但一位朋友让我向他解释这一点,而这实际上归结为Ruby是如何处理变量的,因此我想与他分享一些我为他写的简单图片/解释(抱歉篇幅太长,可能有些过于简单):


问题1:将新变量
str
赋值为
'foo'
时会发生什么情况?

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

问题2:使用
=
将现有变量
str
分配给新对象时会发生什么情况?

答:标签
str
现在指向另一个对象。
问题3:将新变量
=
分配给
str
时会发生什么情况?

答:创建了一个名为
str2
的新标签,该标签指向与
str
相同的对象。
问题4:如果
str
str2
引用的对象发生更改,会发生什么情况?

答:两个标签仍然指向同一个对象,但该对象本身发生了变化(其内容已更改为其他内容)。
这与原来的问题有什么关系? 与第三季度/第四季度的情况基本相同;该方法获取传入它的变量/标签(
str2
)的私有副本(
str
)。它不能更改标签
str
指向的对象,但可以更改它们都引用的对象的内容,以包含其他内容:

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

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

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

[来自3.8.1:对象引用]

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

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

直到最后一段,尤其是最后一句话,我才明白这一切。这充其量是误导,更糟糕的是混淆。对通过值引用传递的对象的修改如何以任何方式改变底层对象

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

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?
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
=>2279146940 Ruby是通过引用2279146940传递的,因为对象id(内存地址)总是相同的;)

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


Ruby被解释。变量是对数据的引用,而不是数据本身。这有助于对不同类型的数据使用相同的变量

分配lhs=rhs,然后复制rhs上的参考,而不是数据。这与其他语言不同,例如C语言,其中赋值将数据从rhs复制到lhs

因此,对于函数调用,传递的变量(比如x)确实被复制到函数中的局部变量中,但x是一个引用。然后将有两个引用副本,都引用相同的数据。一个在调用者中,一个在函数中

然后,函数中的赋值会将新引用复制到函数的x版本。在此之后,调用方的x版本保持不变。它仍然是对原始数据的引用

相反,在x上使用.replace方法将导致ruby进行数据复制。如果在任何新分配之前使用replace,那么调用者也会在其版本中看到数据更改

类似地,只要原始引用与传入的变量保持一致,实例变量就会与调用方看到的相同。在
str2.replace 'baz'
str2 # => 'baz'
str  # => 'baz'
str.object_id # => 2002
str2.object_id # => 2002
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
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 ;)"
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}"
1.object_id
#=> 3

2.object_id
#=> 5

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

b = 2
#=> 2
b.object_id
#=> 5
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
c
#=> 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
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
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
def my_foo(a_hash)
  a_hash["test"]="reference"
end;

hash = {"test"=>"value"}
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"
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;
}
in inc: 6
in main: 5
in inc: 1
in main: 1
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;
}
in replace: 10
in main: 10
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
1
2
2
3
2

1
2
1
Two references refer to same object as long as there is no reassignment. 
    a = "first string"
    b = a



    b.upcase! 
    => FIRST STRING
    a
    => FIRST STRING

    b = "second string"


a
    => FIRST STRING
    hash = {first_sub_hash: {first_key: "first_value"}}
first_sub_hash = hash[:first_sub_hash]
first_sub_hash[:second_key] = "second_value"

    hash
    => {first_sub_hash: {first_key: "first_value", second_key: "second_value"}}

    def change(first_sub_hash)
    first_sub_hash[:third_key] = "third_value"
    end

    change(first_sub_hash)

    hash
    =>  {first_sub_hash: {first_key: "first_value", second_key: "second_value", third_key: "third_value"}}
> astringobject = "lowercase"

> bstringobject = astringobject.upcase
> # bstringobject is a new object created by String.upcase

> puts astringobject
lowercase

> puts bstringobject
LOWERCASE
def foo(bar)
  puts "bar (#{bar}) entering foo with object_id #{bar.object_id}"
  bar =  "reference"
  puts "bar (#{bar}) leaving foo with object_id #{bar.object_id}"
end

bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"

# Output
bar (value) before foo with object_id 60
bar (value) entering foo with object_id 60
bar (reference) leaving foo with object_id 80 # <-----
bar (value) after foo with object_id 60 # <-----
def foo(bar)
  puts "bar (#{bar}) entering foo with object_id #{bar.object_id}"
  bar.replace "reference"
  puts "bar (#{bar}) leaving foo with object_id #{bar.object_id}"
end

bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"

# Output
bar (value) before foo with object_id 60
bar (value) entering foo with object_id 60
bar (reference) leaving foo with object_id 60 # <-----
bar (reference) after foo with object_id 60 # <-----
def foo(fiz)
  puts "fiz (#{fiz}) entering foo with object_id #{fiz.object_id}"
  fiz =  "reference"
  puts "fiz (#{fiz}) leaving foo with object_id #{fiz.object_id}"
end

bar = "value"
puts "bar (#{bar}) before foo with object_id #{bar.object_id}"
foo(bar)
puts "bar (#{bar}) after foo with object_id #{bar.object_id}"

# Output
bar (value) before foo with object_id 60
fiz (value) entering foo with object_id 60
fiz (reference) leaving foo with object_id 80
bar (value) after foo with object_id 60