6

好的,也许这很简单,但是......鉴于此:

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”不是这里块的本地吗?但是它确实影响了对象,怎么会?

4

4 回答 4

11

我将扩展@pst 的评论:

为什么这不起作用?

arr.each { |v| v = "bad" }

因为each遍历数组并将每个项目放入您作为局部变量给出的块中vv而不是对数组的引用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!!

于 2012-10-23T01:50:14.830 回答
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 }
于 2012-10-23T03:01:13.927 回答
3

您可能正在寻找 .map - 它返回一个新数组,其中包含每个元素的块的返回值。

arr.map { "bad" }

=> ["bad", "bad", "bad", "bad", …] 

using.map!将更改原始数组的内容,而不是返回一个新数组。

于 2012-10-23T01:28:00.977 回答
1

这个怎么样

arry = Array.new(arry.length,"bad")

这会将“坏”的默认值设置为arry.length

于 2012-10-23T10:34:46.343 回答