2012-01-27 3 views
6

私は、Ruby 1.9.2-P290を使用していています:私が期待するものではありませんarray.eachの動作がArray.new構文に依存するのはなぜですか?

a = Array.new(2, []).each {|i| i.push("a")}  
=> [["a", "a"], ["a", "a"]] 

を。しかし、次のコンストラクタスタイルは、私が期待することをします:

b = Array.new(2) {Array.new}.each {|i| i.push("b")} 
=> [["b"], ["b"]] 

最初の例は期待された動作ですか?

Ruby-docでは、私のsize=2引数は両方のコンストラクタで同じ種類の引数であるようです。私は、eachメソッドが両方のコンストラクタに対して同じ方法を使用するという引数を渡していると思う。

答えて

10

これはよくある誤解です。あなたの最初の例では、2つの要素を持つ配列を作成しています。 これらは両方ともと同じの配列へのポインタです。あなたの外側の配列を反復処理するときだから、あなたは2回

あなたの出力に反映され、内側の配列に2つの要素を追加し、これらの比較:あなたは、単一のを使用している最初のケースで

> array = Array.new(5, []) 
=> [[], [], [], [], []] 

# Note - 5 identical object IDs (memory locations) 
> array.map { |o| o.object_id } 
=> [70228709214620, 70228709214620, 70228709214620, 70228709214620, 70228709214620] 

> array = Array.new(5) { [] } 
=> [[], [], [], [], []] 

# Note - 5 different object IDs (memory locations) 
> array.map { |o| o.object_id } 
=> [70228709185900, 70228709185880, 70228709185860, 70228709185840, 70228709185780] 
4

をメインアレイの要素のデフォルトとして配列のインスタンス:pushは二度同じインスタンスに適用されるよう

a = Array.new(2, []).each {|i| i.push("a")} 

第2引数は単に、再循環されます。ここでは、引数として提供されるインスタンスを1つだけ作成したので、何度も繰り返し使用されます。

第二の方法は、これを行うための正しい方法である:

b = Array.new(2) {Array.new}.each {|i| i.push("b") 

これは意図的にメインアレイの各位置のための配列の新しいインスタンスを作成します。ここでの重要な違いは、ブロック{ ... }を使用して、新しい配列の各位置に対して1回実行することです。このの短い形式のバージョンは次のようになりますルビーのドキュメントから

b = Array.new(2) { [ ] }.each {|i| i.push("b") 
1

new(size=0, obj=nil) 
new(array) 
new(size) {|index| block } 

は、新しい配列を返します。最初の形式では、新しい配列は空です。 2番目の方法では、objのサイズのコピー(つまり、同じobjへのサイズ参照)で作成されます。 3番目の形式は、パラメータとして渡された配列のコピーを作成します(配列は、パラメータに対してto_aryを呼び出すことによって生成されます)。最後の形式では、指定されたサイズの配列が作成されます。

したがって、あなたが作成a配列で、あなたはこのように同じ配列への2つの参照、それらの両方にpush作品を持っています。つまり、"a"を同じアレイに2回押し込むことになります。作成する配列bでは、実際には各要素の新しい配列を作成しています。

関連する問題