One of the neat things about ruby is that, in general, operators are methods, so they can be overridden.
When working with multidimensional arrays, some languages allow you to do the following:
a[0,0]
whereas ruby uses:
a[0][0]
Which makes sense, since Ruby implements it as an array of arrays and you’re chaining methods to get to the element. But, since we can override the [] method, we can then say a[0,0].
class Grid
attr_reader :cols, :rows
# initialize our grid and optionally prepopulate it.
# klass can behave in the following fashions:
# nil -- nil
# a value (like "", 0, true) -- that value
# a class which responds to new -- an instance of that class
def initialize(rows=1, cols=1, klass = nil)
@rows = rows
@cols = cols
@grid = (0 ... @rows).collect do |y|
(0 ... @cols).collect do |x|
if klass.nil? then
nil
elsif klass.respond_to? :new
klass.new
else
klass
end
end
end
end
# This method allows us to say +grid[x, y]+ instead of +grid[y][x]+
def [](x, y=nil)
(x,y) = x if x.instance_of? Array
@grid[y][x]
end
# This method allows us to set the value of a grid grid entry via
# +grid[x, y]+ instead of +grid[y][x]+
def []=(*args)
if (args[0].instance_of? Array)
((x, y),value) = args
else
(x,y,value) = args
end
@grid[y][x] = value
end
end
So, it can be used like this:
$ irb
>> require 'grid'
=> true
>> g=Grid.new
=> #< Grid:0xb747d760 @cols=1, @rows=1, @grid=[[nil]] >
>> g=Grid.new(2,2)
=> #< Grid:0xb747bb04 @cols=2, @rows=2, @grid=[[nil, nil], [nil, nil]] >
>> g=Grid.new(2,2,0)
=> #< Grid:0xb7479aac @cols=2, @rows=2, @grid=[[0, 0], [0, 0]] >
>> g=Grid.new(2,2,String)
=> #< Grid:0xb7477810 @cols=2, @rows=2, @grid=[["", ""], ["", ""]] >
>> g=Grid.new(2,2,false)
=> #< Grid:0xb74755d8 @cols=2, @rows=2, @grid=[[false, false], [false, false]] >
>> g[0,0]
=> false
>> g[0,0]=true
=> true
>> g
=> #< Grid:0xb74755d8 @cols=2, @rows=2, @grid=[[true, false], [false, false]] >
It’s an interesting technique — and given that most people think of arrays/coordinates in pairs, I think that it can make the code more “readable”.