@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
对象在lang_errors
方法中为update_lanugages
变量添加了错误。当我对@user
对象执行保存时,我丢失了最初存储在lang_errors
变量中的错误。
虽然我试图做的更多是一个黑客(似乎没有工作)。我想了解为什么变量值被淘汰了。我理解通过引用传递,所以我想知道如何在不被淘汰的情况下将该值保存在该变量中。
在传统术语中,Ruby is strictly pass-by-value。但那不是你在这里问的那个。
Ruby没有任何纯粹的非引用值的概念,所以你当然不能将一个传递给一个方法。变量始终是对象的引用。为了获得一个不会从你下面改变的对象,你需要复制或克隆你传递的对象,从而给出一个没有其他人可以引用的对象。 (尽管这不是防弹的 - 但两种标准克隆方法都做了浅拷贝,因此克隆的实例变量仍然指向与原件相同的对象。如果ivars引用的对象发生变异,那么仍然显示在副本中,因为它引用了相同的对象。)
试试这个: -
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
这里标识符是返回值对象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
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变量不包含值,但包含对value对象的引用。
应该注意的是,您甚至不必使用“替换”方法来更改值原始值。如果为散列指定其中一个散列值,则表示您正在更改原始值。
def my_foo(a_hash)
a_hash["test"]="reference"
end;
hash = {"test"=>"value"}
my_foo(hash)
puts "Ruby is pass-by-#{hash["test"]}"
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"}}
其他的答案都是正确的,但是一位朋友让我向他解释这个问题以及它真正归结为Ruby如何处理变量,所以我想我会分享一些我为他写的简单图片/解释(道歉的长度)并且可能有些过于简单化了):
str
to a value of 'foo'
?str = 'foo'
str.object_id # => 2000
str
is created that points at the object 'foo'
, which for the state of this Ruby interpreter happens to be at memory location 2000
.str
to a new object using =
?str = 'bar'.tap{|b| puts "bar: #{b.object_id}"} # bar: 2002
str.object_id # => 2002
str
now points to a different object.=
to str
?str2 = str
str2.object_id # => 2002
str2
is created that points at the same object as str
.str
and str2
gets changed?str2.replace 'baz'
str2 # => 'baz'
str # => 'baz'
str.object_id # => 2002
str2.object_id # => 2002
它与Q3 / Q4中的情况基本相同;该方法获取其传递给它的变量/标签(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
Ruby通过引用或值传递吗?
Ruby是按值传递的。总是。没有例外。不,如果。没有但是。
这是一个简单的程序,它证明了这一事实:
def foo(bar)
bar = 'reference'
end
baz = 'value'
foo(baz)
puts "Ruby is pass-by-#{baz}"
# Ruby is pass-by-value
(使用Python的术语。)
要说Ruby使用“按值传递”或“按引用传递”并不具有足够的描述性,无法提供帮助。我认为,如今大多数人都知道,术语(“价值”与“参考”)来自C ++。
在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;
}
输出:
in inc: 6
in main: 5
in inc: 1
in main: 1
在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;
}
输出:
in replace: 10
in main: 10
如果参数不是对象,Ruby使用pass by value(在C ++意义上)。但是在Ruby中,一切都是一个对象,所以在Ruby中C ++意义上确实没有值得传递。
在Ruby中,使用“通过对象引用传递”(使用Python的术语):
因此,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
输出:
1
2
2
3
2
1
2
1
*这就是为什么在Ruby中,如果要修改函数内部的对象但在函数返回时忘记这些更改,则必须在对副本进行临时更改之前显式创建对象的副本。
Ruby是严格意义上的值传递,但值是引用。
这可以称为“按值传递参考”。这篇文章有我读过的最好的解释:http://robertheaton.com/2014/07/22/is-ruby-pass-by-reference-or-pass-by-value/
按值传递可以简要解释如下:
函数接收对(并将访问)内存中与调用者使用的相同对象的引用。但是,它不会收到调用者正在存储此对象的框;在pass-value-by-value中,函数提供自己的框并为自己创建一个新变量。
由此产生的行为实际上是传递引用和传值的经典定义的组合。
已经有了一些很好的答案,但我想发布关于这个主题的一对权威的定义,但也希望有人可以解释一下当局Matz(Ruby的创造者)和David Flanagan在他们出色的O'Reilly书中所说的内容, Ruby编程语言。
[来自3.8.1:对象引用]
将对象传递给Ruby中的方法时,它是传递给方法的对象引用。它不是对象本身,也不是对对象引用的引用。另一种说法是方法参数是通过值而不是通过引用传递的,但传递的值是对象引用。
由于对象引用传递给方法,因此方法可以使用这些引用来修改基础对象。当方法返回时,这些修改随后可见。
这一切对我来说都是有意义的,直到最后一段,尤其是最后一句。这充其量是误导性的,更糟糕的是混淆。无论如何,如何修改传递值的引用会改变底层对象?
Ruby通过引用或值传递吗?
Ruby是传递引用。总是。没有例外。不,如果。没有但是。
这是一个简单的程序,它证明了这一事实:
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是传递引用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}"
=>有些人没有意识到它的参考,因为本地分配可以优先,但它显然是通过参考传递
参数是原始参考的副本。因此,您可以更改值,但不能更改原始引用。
Ruby被解释了。变量是对数据的引用,但不是数据本身。这便于对不同类型的数据使用相同的变量。
lhs = rhs的赋值然后复制rhs上的引用,而不是数据。这在其他语言中有所不同,例如C,其中赋值从rhs执行数据复制到lhs。
因此对于函数调用,传递的变量,比如x,确实被复制到函数中的局部变量中,但x是一个引用。然后将有两个引用副本,两个引用相同的数据。一个将在调用者中,一个在函数中。
然后函数中的赋值将复制对函数x版本的新引用。在此之后,调用者的x版本保持不变。它仍然是原始数据的参考。
相反,在x上使用.replace方法将导致ruby进行数据复制。如果在任何新分配之前使用replace,那么调用者确实也会在其版本中看到数据更改。
类似地,只要原始引用与传入的变量一致,实例变量将与调用者看到的相同。在对象的框架内,实例变量始终具有最新的引用值,无论这些引用值是由调用者提供还是在传入类的函数中设置的。
由于对'='的混淆,'按值调用'或'按引用调用'在这里混乱。在编译语言中,'='是数据副本。在这种解释语言中,'='是参考副本。在示例中,您传入的引用后跟一个引用副本,但通过'='来破坏引用中传递的原始内容,然后人们谈论它就像'='一样是数据副本。
为了与定义保持一致,我们必须与'.replace'保持一致,因为它是一个数据副本。从'.replace'的角度来看,我们看到这确实是通过引用传递的。此外,如果我们在调试器中进行操作,我们会看到传入的引用,因为变量是引用。
但是,如果我们必须保持'='作为参考框架,那么我们确实可以看到传入的数据直到分配,然后我们在分配后不再看到它,而调用者的数据保持不变。在行为级别,只要我们不认为传入的值是复合的,这就是值传递 - 因为在更改单个赋值中的其他部分时我们将无法保留其中的一部分(作为该赋值)更改引用,原始范围超出范围)。还会有一个疣,在这种情况下,对象中的变量将是引用,所有变量也是如此。因此,我们将被迫谈论传递“按价值引用”并且必须使用相关的位置。