@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!
!