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指定したブロックに各アイテムを配置するためです。vvarr

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"、配列の最初のインデックスには割り当てていません。配列への参照です。ブロックの中に入れて、あなたはそれを変えることができます:arrarr

arr.each { |v| 
  arr[0] = "bad" # yes, a bad idea!
}

では、なぜarr.each { |p| p.age = 50 }配列内のアイテムを更新するのですか?p配列に含まれているオブジェクトを参照しているためです。最初の反復では、pとしても知られているオブジェクトを参照し、メソッドがあり、それに固執します。は配列の最初の項目でもありますが、配列ではありません。あなたはこれを行うことができます:kidkidage=50kidkid

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

于 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", …] 

を使用.map!すると、新しい配列を返すのではなく、元の配列の内容が変更されます。

于 2012-10-23T01:28:00.977 に答える
1

これはどう

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

これにより、デフォルト値の「bad」がarry.lengthに設定されます。

于 2012-10-23T10:34:46.343 に答える