好吧也许这很简单但...... 鉴于此:
arr = ("a".."z").to_a
arr
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
..并且我正在尝试将所有“arr”值更改为“bad”
为什么 这不起作用?
arr.each { |v| v = "bad" }
arr
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
答案提示“v”是块的局部变量(数组值的“副本”),我完全明白(以前从未困惑过我)但是
..如果数组元素是对象,它为什么会起作用?
class Person
def initialize
@age = 0
end
attr_accessor :age
end
kid = Person.new
man = Person.new
arr = [kid, man]
arr.each { |p| p.age = 50 }
arr[0]
=> #<Person:0xf98298 @age=50>
这里的“p”还不在这里? 但是它真的影响了对象,为什么会这样?
答案 0 :(得分:11)
我将扩展@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
中的每个项目/对象填充了局部变量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
返回一个数组,其中填充了块的返回值。 map!
将使用填充了块的返回值的数组更新您调用它的引用。一般来说,无论如何迭代它时更新对象是个坏主意。我发现将它想象为创建一个新数组总是更好,然后你可以使用!
方法作为捷径。
答案 1 :(得分:4)
在示例中
arr.each { |v| v = "bad" }
“v”只是对字符串的引用,当你执行v = "bad"
时,你重新分配局部变量。为了使一切变得糟糕,你可以这样做:
arr.each { |v| v.replace "bad" }
下次您可以使用Object#object_id
puts arr[0].object_id #will be save as object_id in first iteration bellow
arr.each { |v| puts v.object_id }
答案 2 :(得分:3)
您可能正在寻找.map - 它返回一个新数组,其中包含每个元素的块的返回值。
arr.map { "bad" }
=> ["bad", "bad", "bad", "bad", …]
使用.map!
将改变原始数组的内容,而不是返回一个新数组。
答案 3 :(得分:1)
这个怎么样
arry = Array.new(arry.length,"bad")
这会将默认值“bad”设置为 arry.length