Thursday, February 23, 2006

Ruby arrays and matrices

One of the first things you learn when reading a book on Ruby is that "variables" hold references to objects. Consider
a = Foo.newb = a
Both a and b contain the same instance of the object. Fine. Simple. Java does the same and so does Python. You just know what that means and you know that if you want a copy instead (that is to say b and a should change indipendently) you must dup or clone them.
a = Foo.newb = a.dup
Now it comes to arrays. Ruby arrays are much like Python list. They are powerful objects. And have something Python lists do not have, some more constructor. From the ruby doc:
Array::newArray.new(size=0, obj=nil)Array.new(array)Array.new(size) {|index| block }
Returns a new array. In the first form, the new array is empty. In the second it is created with size copies of obj (that is, size references to the same obj). The third form creates a copy of the array passed as a parameter (the array is generated by calling to_ary on the parameter). In the last form, an array of the given size is created. Each element in this array is calculated by passing the element's index to the given block and storing the return value.
We analyze the very first constructor. It makes perfectly sense: consider this:
l = Array.new(5, "") # ["", "", "", "", ""]
l[1] ="foo"
l # ["", "foo", "", "", ""]
Now consider this:
l = Array.new(5, []) # [[], [], [], [], []]
els = %w{ el1 el2 el3 el4 el5 }
l.each_index { | index | l[index].push els[index] }
# [
# ["el1", "el2", "el3", "el4", "el5"],
# ["el1", "el2", "el3", "el4", "el5"],
# ["el1", "el2", "el3", "el4", "el5"],
# ["el1", "el2", "el3", "el4", "el5"],
# ["el1", "el2", "el3", "el4", "el5"]
# ]
This is the correct result. But this probably is not what we wanted to obtain. The "correct way to initialize l" would have been
l = Array.new(5) { | dummy | Array.new } # [[], [], [], [], []]
els = %w{ el1 el2 el3 el4 el5 }
l.each_index {| index | l[index].push els[index] }
# [["el1"], ["el2"], ["el3"], ["el4"], ["el5"]]
This time for each line we are creating a "new" empty list, not the very same one.
In this case you probably should have been using the Matrix class, anyway.

No comments: