Suprise in Ruby

I can not remember having read about it before, however the following
happens here:

irb(main):015:0> arr = Array.new(3, Array.new)
[[], [], []]
irb(main):016:0> arr[0].push(1)
[1]
irb(main):017:0> arr
[[1], [1], [1]]

ruby 1.8.5

but
irb(main):018:0> arr1 = Array.new(3)
[nil, nil, nil]
irb(main):019:0> 0.upto(2) {|i| arr1[i] = Array.new}
0
irb(main):020:0> arr1
[[], [], []]
irb(main):021:0> arr1[0].push(1)
[1]
irb(main):022:0> arr1
[[1], [], []]

I’m not getting it. Is it supposed to work that way?

Regards
Friedrich

Friedrich D. [email protected]
writes:

irb(main):015:0> arr = Array.new(3, Array.new)
[[], [], []]
irb(main):016:0> arr[0].push(1)
[1]
irb(main):017:0> arr
[[1], [1], [1]]

I’m not getting it. Is it supposed to work that way?

I think so… see ri Array.new

In your first test, all array items refer to the same
default obj. If it happens to be modified, this modification affects all
the
element that refer to this default object.

In you second test, the block create a new copy for each
element. Modifying one of them, doesn’t affect the others.

On Thu, 9 Nov 2006, Friedrich D. wrote:

I can not remember having read about it before, however the following
happens here:

irb(main):015:0> arr = Array.new(3, Array.new)

[…]

irb(main):018:0> arr1 = Array.new(3)
[nil, nil, nil]
irb(main):019:0> 0.upto(2) {|i| arr1[i] = Array.new}
0

[…]

I’m not getting it. Is it supposed to work that way?

Yes. In your first call, ‘Array.new’ is evaluated ONCE before the call,
and hence all array members get initialised to use the SAME array.

In your second example, you call Array.new once per array index, to make
sure every array index gets its own array assigned to it.

There is a slightly simpler way to achieve your desired behaviour,
though:

irb(main):004:0> arr=Array.new(3) { Array.new }
=> [[], [], []]
irb(main):005:0> arr[0].push(1)
=> [1]
irb(main):006:0> arr
=> [[1], [], []]

In this case, you’d be passing in Array.new as a block - which gets
evaluated ONCE per value in the array you’re creating…

To make it a bit more apparent, see the difference between:

irb(main):011:0> i=0
=> 0
irb(main):012:0> Array.new(3,i+=1)     # i+=1 gets evaluated ONCE
=> [1, 1, 1]

and

irb(main):013:0> i=0
=> 0
irb(main):014:0> Array.new(3) { i+=1 } # i+=1 gets evaluated once PER 

array item
=> [1, 2, 3]

The difference simply being that in the first case, your initialiser
gets
evaluated just once immediately before calling Array.new; and in the
second case, Array.new calls your block once PER array item that gets
created…

Benedikt

ALLIANCE, n. In international politics, the union of two thieves who
have their hands so deeply inserted in each other’s pockets that
they cannot separately plunder a third.
(Ambrose Bierce, The Devil’s Dictionary)

Benedikt H. [email protected] writes:

[nil, nil, nil]
irb(main):019:0> 0.upto(2) {|i| arr1[i] = Array.new}
0

[…]

I’m not getting it. Is it supposed to work that way?

Yes. In your first call, ‘Array.new’ is evaluated ONCE before the call,
and hence all array members get initialised to use the SAME array.
Yeah it has come to me this night that it’s supposed to be right.

Anyway thanks for taking the time to answer

Regards
Friedrich

Hi –

On Thu, 9 Nov 2006, Friedrich D. wrote:

ruby 1.8.5
irb(main):022:0> arr1
[[1], [], []]

I’m not getting it. Is it supposed to work that way?

Yes. When you do this:

Array.new(n, obj)

you get an array of size n, with each element initialized to obj. obj
is only evaluated once, so if it’s “Array.new”, you get the same array
for all the elements.

David