0

I have a method:

def test
    f = Array.new(4,Array.new(4,false))
    f.each do |array|
        p array.inspect
    end
    f[1][1] = true
    p "after setting f[1][1]"
    f.each do |array|
        p array.inspect
    end
end

This is the output:

"[false, false, false, false]"
"[false, false, false, false]"
"[false, false, false, false]"
"[false, false, false, false]"
"after setting f[1][1]"
"[false, true, false, false]"
"[false, true, false, false]"
"[false, true, false, false]"
"[false, true, false, false]"

As you can see all values in col 1 became true, why could that happen???

AlexLuo
  • 458
  • 1
  • 4
  • 12
  • No, not a bug. `Array.new(4, Array.new(4,false))` sets each row of `f` to the *same* 4-element array from `Array.new(4,false)`. It doesn't execute `Array.new(4,false)` 4 times. So `f` is an array of 4 references to the same 4-element object. – lurker Dec 12 '17 at 19:22
  • 1
    Yes, your code has a bug. – Stefan Pochmann Dec 12 '17 at 19:23
  • @lurker hah, that's really tricky. Thank you for pointing out. – AlexLuo Dec 12 '17 at 19:24
  • Your example is pretty much the exact same example as the one in the documentation, which explains this exact behavior. It has also been asked and answered numerous times already on [so]. – Jörg W Mittag Dec 12 '17 at 19:47

1 Answers1

1

It is not a bug. Array.new(4, Array.new(4,false)) creates an array of 4 elements where each element is the same array object determined by a single call to Array.new(4, false). It does not execute Array.new(4,false) 4 times, once for each element of f. So you end up with f as an array of 4 references to the same object (a single 4-element array, [false, false, false, false]).

If you want to have an array of 4 different 4-element arrays, there are many ways to do this in Ruby. One way is:

f = Array.new(4) { Array.new(4, false) }

This will execute Array.new(4, false) separately for each entry in your Array.new(4).

2.4.0 :002 >  f = Array.new(4) { Array.new(4, false) }
 => [[false, false, false, false], [false, false, false, false], [false, false, false, false], [false, false, false, false]]
2.4.0 :003 > f.each do |array|
2.4.0 :004 >   p array.inspect
2.4.0 :005?> end
"[false, false, false, false]"
"[false, false, false, false]"
"[false, false, false, false]"
"[false, false, false, false]"
 => [[false, false, false, false], [false, false, false, false], [false, false, false, false], [false, false, false, false]]
2.4.0 :006 > f[1][1] = true
 => true
2.4.0 :007 > p "after setting f[1][1]"
"after setting f[1][1]"
 => "after setting f[1][1]"
2.4.0 :008 > f.each do |array|
2.4.0 :009 >   p array.inspect
2.4.0 :010?> end
"[false, false, false, false]"
"[false, true, false, false]"
"[false, false, false, false]"
"[false, false, false, false]"
 => [[false, false, false, false], [false, true, false, false], [false, false, false, false], [false, false, false, false]]
2.4.0 :011 >

For reference, see the Ruby documentation for Creating Arrays.

lurker
  • 56,987
  • 9
  • 69
  • 103