@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_languages方法中的lang_errors变量中。 当我在@user对象上执行保存时,我丢失了最初存储在lang_errors变量中的错误。

虽然我正在尝试做的更多的是一个黑客(似乎没有工作)。我想知道为什么变量值被洗掉了。我理解通过引用传递,所以我想知道值如何可以保存在那个变量中而不被洗掉。


当前回答

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

赋值lhs = rhs然后复制rhs上的引用,而不是数据。这与其他语言不同,比如C语言,赋值是从rhs复制数据到lhs。

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

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

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

类似地,只要传入变量的原始引用是恰当的,则调用者看到的实例变量将是相同的。在对象的框架内,实例变量总是具有最新的引用值,无论这些引用值是由调用者提供的,还是在类传入的函数中设置的。

“按值调用”或“按引用调用”在这里被混淆了,因为在编译语言中“=”是一个数据副本。在这种解释语言中,'='是一个参考副本。在这个例子中,你有一个传入的引用,后面跟着一个引用副本,尽管'='破坏了传入的原始引用,然后人们谈论它就好像'='是一个数据副本。

为了与定义保持一致,我们必须与'保持一致。替换',因为它是一个数据副本。从的角度来看。替换'我们看到这确实是通过引用传递的。此外,如果我们在调试器中遍历,我们会看到传入的引用,因为变量是引用。

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.

其他回答

需要注意的是,您甚至不需要使用“replace”方法来更改原始值。如果你为一个哈希值分配了其中一个哈希值,你就是在改变原始值。

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

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

试试这个:——

1.object_id
#=> 3

2.object_id
#=> 5

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

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

标识符a包含值对象1的object_id 3,标识符b包含值对象2的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

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

执行以下操作也会给出错误:——

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

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

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

foo中的Arg被赋值为x。 它清楚地表明参数是由值11传递的,值11本身是一个对象,对象id为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

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

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

是的,但是....

Ruby将引用传递给一个对象,因为Ruby中的所有东西都是对象,那么你可以说它是通过引用传递的。

我不同意这里的帖子声称它是通过价值,这对我来说似乎是迂腐的赛门铁克游戏。

然而,实际上它“隐藏”了行为,因为ruby提供的大多数操作都是“开箱即用”的——例如字符串操作,会生成对象的副本:

> astringobject = "lowercase"

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

> puts astringobject
lowercase

> puts bstringobject
LOWERCASE

这意味着大多数时候,原始对象保持不变,给人一种ruby是“传递值”的感觉。

当然,在设计自己的类时,理解这种行为的细节对于功能性行为、内存效率和性能都很重要。

关于Ruby的“按值传递引用”如何工作的理论,有很多很棒的答案。但以身作则,我能更好地学习和理解一切。希望这对你们有帮助。

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

正如你所看到的,当我们输入这个方法时,我们的工具条仍然指向字符串“value”。但是之后我们给bar分配了一个字符串对象“reference”,它有一个新的object_id。在这种情况下,foo内部的bar具有不同的作用域,无论我们在方法中传递什么,当我们重新分配它并将其指向内存中包含String "reference"的新位置时,bar都不再访问它。

现在考虑同样的方法。唯一的区别是在方法中使用do

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

注意到区别了吗?我们在这里所做的是:我们修改了String对象的内容,变量所指向的对象。bar的范围在方法内部仍然是不同的。

所以要注意如何处理传递给方法的变量。如果修改传入的变量-in-place (gsub!, replace, etc),然后在方法的名称中以砰的一声表示so,比如“def foo!”

注:

重要的是要记住,foo的“bar”的内部和外部是“不同的”“bar”。它们的范围不同。在该方法中,您可以将“bar”重命名为“club”,结果将是相同的。

我经常看到变量在方法内部和外部被重用,虽然这很好,但它降低了代码的可读性,是一种代码气味。我强烈建议不要做我在上面例子中所做的:),而是这样做

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

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

赋值lhs = rhs然后复制rhs上的引用,而不是数据。这与其他语言不同,比如C语言,赋值是从rhs复制数据到lhs。

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

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

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

类似地,只要传入变量的原始引用是恰当的,则调用者看到的实例变量将是相同的。在对象的框架内,实例变量总是具有最新的引用值,无论这些引用值是由调用者提供的,还是在类传入的函数中设置的。

“按值调用”或“按引用调用”在这里被混淆了,因为在编译语言中“=”是一个数据副本。在这种解释语言中,'='是一个参考副本。在这个例子中,你有一个传入的引用,后面跟着一个引用副本,尽管'='破坏了传入的原始引用,然后人们谈论它就好像'='是一个数据副本。

为了与定义保持一致,我们必须与'保持一致。替换',因为它是一个数据副本。从的角度来看。替换'我们看到这确实是通过引用传递的。此外,如果我们在调试器中遍历,我们会看到传入的引用,因为变量是引用。

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.