我将扩展@pst 的评论:
为什么这不起作用?
arr.each { |v| v = "bad" }
因为each
遍历数组并将每个项目放入您作为局部变量给出的块中v
,v
而不是对数组的引用arr
。
new_arr = arr.each { |v| v = "bad" }
each
不返回数组,因为您会使用它map
(请参阅@benjaminbenben 的答案)。因此分配它不会“工作”。
arr.each { |v| arr[arr.index v] = "bad" }
在这里,您将每个项目arr
放入局部变量v
中,但您还在块中引用了数组本身,因此您可以分配给数组并使用局部变量v
来查找与v
(但是您可能会发现,当项目并非都是唯一的时,这不会像您期望的那样工作)。
arr.each { |p| p.age = 50 }
kid.age #-> 50
在这里,您再次p
用 in 中的每个项目/对象填充了局部变量arr
,但随后您通过方法访问了每个项目,因此您可以更改该项目 -您没有更改数组。这是不同的,因为引用是对局部变量的内容,您将其与对数组的引用混为一谈。它们是分开的东西。
回应以下评论:
arr[0]
# => #<Person:0xf98298 @age=50>
这完全取决于谁在何时指代谁。
尝试这个:
v = Person.new
# => #<Person:0x000001008de248 @age=0>
w = Person.new
# => #<Person:0x000001008d8050 @age=0>
x = v
# => #<Person:0x000001008de248 @age=0>
v = Person.new
# => #<Person:0x00000100877e80 @age=0>
arr = [v,w,x]
# => [#<Person:0x00000100877e80 @age=0>, #<Person:0x000001008d8050 @age=0>, #<Person:0x000001008de248 @age=0>]
v
在那里提到了2个不同的对象。v
不是一个固定的东西,它是一个名字。一开始是指#<Person:0x000001008de248 @age=0>
,后来是指#<Person:0x00000100877e80 @age=0>
。
现在试试这个:
arr.each { |v| v = "bad" }
# => [#<Person:0x00000100877e80 @age=0>, #<Person:0x000001008d8050 @age=0>, #<Person:0x000001008de248 @age=0>]
它们都是对象,但没有任何更新或“工作”。为什么?因为当第一次进入块时,v
指的是数组中产生(给定)的项目。所以第一次迭代v
是#<Person:0x00000100877e80 @age=0>
.
但是,我们然后分配"bad"
给v
。我们没有分配"bad"
给数组的第一个索引,因为我们根本没有引用数组。arr
是对数组的引用。放入arr
块内,您可以更改它:
arr.each { |v|
arr[0] = "bad" # yes, a bad idea!
}
那么为什么要arr.each { |p| p.age = 50 }
更新数组中的项目呢?因为p
指的是也恰好在数组中的对象。在第一次迭代p
时指的是对象也称为kid
, 并且kid
有一个age=
方法并且你坚持50
它。kid
也是数组中的第一项,但您说的kid
不是数组。你可以这样做:
arr.each { |p| p = "bad"; p.age }
NoMethodError: undefined method `age' for "bad":String
起初,p
引用也恰好在数组中的对象(这是它产生的地方),但后来p
被引用为"bad"
.
each
遍历数组并在每次迭代时产生一个值。你只得到值而不是数组。如果你想更新一个数组,你可以这样做:
new_arr = arr.map{|v| v = "bad" }
new_arr = arr.map{|v| "bad" } # same thing
或者
arr.map!{|v| v = "bad"}
arr.map!{|v| "bad"} # same thing
as返回一个填充了块返回值的map
数组。将使用填充了 block 的返回值的数组更新您调用它的引用。通常,无论如何在迭代对象时更新对象是个坏主意。我发现将其视为创建一个新数组总是更好,然后您可以将这些方法用作快捷方式。map!
!