Get method in Array subclass: where's it defined?

Hi,

Daniel F. in another tread help me define a Matrix of essentially
arbitrary dimension as a subclass of Array. He employs a “get”
method to handle a splat of the subscripts. I can’t figure out where
this get method is defined. Does anyone have any idea? Code with
examples follow.

Thanks in advance,
Richard

class Matrix < Array
def [] *args
if (args.length == 2) && args[0].is_a?(Integer) && args[1].is_a?
(Integer)
get(*args)
else
super *args
end
end
end

m = Matrix[ [10,20,30], [40,50,60], [70,80,90] ];
puts m [0] [1].inspect # 20
puts m [2] [0].inspect # 70
puts m[1].inspect # [40, 50, 60]

RichardOnRails wrote:

m = Matrix[ [10,20,30], [40,50,60], [70,80,90] ];
puts m [0] [1].inspect # 20
puts m [2] [0].inspect # 70
puts m[1].inspect # [40, 50, 60]

There is no get method in Array. The get method comes from your original
post, where you defined a get method. Daniel is just saying that under
some conditions your [] method calls the original [] method, under
others (the conditions you define) it calls your get method.

On May 6, 5:20 pm, Tim H. [email protected] wrote:

Richard
end


RMagick:http://rmagick.rubyforge.org/
RMagick 2:http://rmagick.rubyforge.org/rmagick2.html

Ti> m,

Thank you very much for your response.

The get method comes from your original
post, where you defined a get method.

I didn’t interpret it that way.

But that interpretation would yield inconsistent results: 1-based
indexing wit two subscripts using my get function, and 0-based
subscripting with a single subscripts.

Secondly, my adaptation of Daniel’s suggestion, including sub-
classing, works with no definition of get anywhere. And the code runs
without any visible definition of get - certainly not mine.

So I still wish someone would show where this visible undefined get
is, in fact, defined. In fact, it does something with the “splat”
operator … and then I’m lost.

So if you’ve got any other ideas, I’d like to hear them. Again,
thanks for your input.

Best wishes,
Richard

On May 6, 8:51 pm, RichardOnRails
[email protected] wrote:

Secondly, my adaptation of Daniel’s suggestion, including sub-
classing, works with no definition of get anywhere. And the code runs
without any visible definition of get - certainly not mine.

So I still wish someone would show where this visible undefined get
is, in fact, defined. In fact, it does something with the “splat”
operator … and then I’m lost.

Is this under vanilla Ruby or Rails? It simply doesn’t work as you
claim under Ruby alone:

irb(main):001:0> class Matrix < Array; end
=> nil
irb(main):002:0> m = Matrix.new
=> []
irb(main):003:0> m.methods.grep /get/
=> [“instance_variable_get”]
irb(main):004:0> m.get
NoMethodError: undefined method `get’ for []:Matrix
from (irb):4
from :0

RichardOnRails wrote:

m = Matrix[ [10,20,30], [40,50,60], [70,80,90] ];
puts m [0] [1].inspect # 20
puts m [2] [0].inspect # 70
puts m[1].inspect # [40, 50, 60]
[…]
my adaptation of Daniel’s suggestion, including sub-
classing, works with no definition of get anywhere. And the code runs
without any visible definition of get

Your code “works with no definition of get anywhere” because it doesn’t
utilize this ‘get’. When you do “puts m [0] [1]” you aren’t triggering
your ‘get’. That’s because “m [0] [1]” is the plain-vanila array
indexing; it uses one-dimensional indexing. It’s exactly like doing “row
= m[0]; row[1]”. Multi-dimentional array indexing means: “m[0,1]”. and
if you tried that you’d trigger the call to ‘get’, and since it’s
absence, Ruby would complain.

Implementiong your ‘get’ is no big deal. For a start, replace that
“get(*args)” with “self[args[0]][args[1]]”.

On May 6, 11:07 pm, Phrogz [email protected] wrote:

NoMethodError: undefined method `get’ for []:Matrix
from (irb):4
from :0

Hi Phroz,

Thank you for responding.

Is this under vanilla Ruby or Rails?

I think I’ve got “vanilla Ruby”. In January '08, I wiped out my old
version of Ruby and used the ruby186-26_rc2.exe installer for
Windows. It came with Rails 2.0.2 or I installed Rails subsequently.

Albert suggested thst the “get” was never invoked, which I’ve
subsequently verified with the Ruby debugger. (I should have thought
to do that in the first place.) I put in trace statements to
demonstrate the real problem: “[]*args” returns a unitary array
containing the first subscript in each of my invocations.

If you’re still dubious about it working as I indicated, below is my
debugging version and it’s output.

Best wishes,
Richard

====================
Instrumented Program

TA.rb

K:_Projects\Ruby_Ruby_Techniques\Sudoku\TA.rb

class Matrix < Array
def [] *args
puts “args = ‘#{args}’, an #{args.class.to_s} object with length
#{args.length }”
if (args.length == 2) && args[0].is_a?(Integer) && args[1].is_a?
(Integer)
get(*args)
else
puts ‘In “def [] *args”, “else” clause’
super *args # raise IndexError? #
end
end
end

m = Matrix[ [10,20,30], [40,50,60], [70,80,90] ]
puts m [0] [1].inspect # 20
puts m [2] [0].inspect # 70
puts m[1].inspect # [40, 50, 60]

=====================
Command Window output

K:_Projects\Ruby_Ruby_Techniques\Sudoku>ruby ta.rb
args = ‘0’, an Array object with length 1
In “def [] *args”, “else” clause
20
args = ‘2’, an Array object with length 1
In “def [] *args”, “else” clause
70
args = ‘1’, an Array object with length 1
In “def [] *args”, “else” clause
[40, 50, 60]

K:_Projects\Ruby_Ruby_Techniques\Sudoku>ta.rb

K:_Projects\Ruby_Ruby_Techniques\Sudoku>ruby ta.rb
args = ‘0’, an Array object with length 1
In “def [] *args”, “else” clause
20
args = ‘2’, an Array object with length 1
In “def [] *args”, “else” clause
70
args = ‘1’, an Array object with length 1
In “def [] *args”, “else” clause
[40, 50, 60]

K:_Projects\Ruby_Ruby_Techniques\Sudoku>

Hi,

  • is the splat operator. It means that all of the arguments to the
    method
    will go in the args variable. None of the def [] part affects this.

If you call [] with the arguments 1, 2, and 3, then args will be an
array
with the values 1, 2, and 3.
If you call [] with the arguments 1 and 3, then args will be an array
with
the values 1 and 3.
If you call [] with one argument, then args will be an array containing
only
that argument.
If you call [] with no arguments, then args will be an empty array.

This behavior is useful because you don’t know how many arguments [] was
called with.

So, when you do args.length == 2, you make sure that the [] method was
called with two arguments. When you do args[0].is_a? Integer, you make
sure
that the first argument given was an integer, and likewise with the 2nd
args[1].is_a? integer.

Conversely, when you do get(*args), you use the splat operator in
reverse.
Instead of taking arguments and putting them into an array, you take an
array and put use its elements as arguments like so:

def max(a, b)
if a > b
a
else
b
end
end

max(2, 3) #=> 3
max(3, 2) #=> 3
an_array = [3, 2]
max(an_array) #=> Error because you only gave one argument, the array
max(*an_array) #=> 3 because the array was split into arguments.

On another note (sorry if this confuses you), a more idomatic way to
write
args[0].is_a? Integer and args[1].is_a? Integer is this:
args.all? {|arg| arg.is_a? Integer }

Dan

On 5/7/08, RichardOnRails [email protected]

On May 7, 4:06 am, Albert S. [email protected] wrote:

Your code “works with no definition of get anywhere” because it doesn’t
Posted viahttp://www.ruby-forum.com/.
Hi Albert,

As you obviously recognized that I was totally out at sea on this
thing. The instrumented version, which I added in my reply to Phroz,
confirms your assessment that “get” was never invoked.

While I’m grateful for that revelation, I am ever more grateful for
your additional, thorough analysis of the situation. I will employ
them to good effect.

What’s really at the root of my problems with this thing is my
ignorance about the def [] * args.

It appears that what’s being defined is “args”. I concluded this by
changing its name and drawing an “undefined local variable or method
`args’” error message for the next line. To me, that seems weird. It
looks like C or C++ with type info before the name of a variable being
defined.

I can’t find “def [] *” on the net. Can you shed any light on this?

Best wishes,
Richard

On May 7, 12:41 pm, RichardOnRails
[email protected] wrote:

my adaptation of Daniel’s suggestion, including sub-

looks like C or C++ with type info before the name of a variable being
defined.

I can’t find “def [] *” on the net. Can you shed any light on this?

Best wishes,
Richard

Hello, again, Daniel and Albert,

You’ve both been very gracious and generous in educating me in the
Ruby Way (to borrow Hal F.'s apt title). So far I’ve only written
“toy” Ruby apps, but on those occasions I strive to write the best
Ruby I can. Too often, that effort becomes insoluble solely with my
books and web searches.

Your wonderful tutorials have elevated my Ruby skills, for which I am
very grateful. I now understand all the nuances that had troubled
me. I hope others stumble across this thread and benefit from it as I
have.

Thanks and Best wishes,
Richard Muller

Daniel F. was faster than me :wink: but I’ll my answer nevertheless; it
duplicates some of his.

RichardOnRails wrote:

[…] is my ignorance about the def [] * args.

There are two unrelated features in that syntax: the ‘*’ and the ‘[]’.

Let’s start with a simple method that accept one argument:

def simple(one)
puts one
end

Now, let’s extend it to accept two arguments:

def simple(one, two)
puts one, two
end

But what if we wanted this method to accept any number of arguments?
We would use the ‘*’ syntax, that tells Ruby to lumb all arguments into
an array:

def simple(*all)
all.each { |i|
puts i
}
end

We can invoke this method thus:

simple 4
simple
simple 9, 4, “blah”
simple -3, 5.6

======

That’s all for the ‘*’. Now, for the ‘[]’:

let’s look at this expression:

m[4]

The ‘m’ object would return the 4th element. It has to have some method
that responds to this request. Ruby would invoke this method whenever
the ‘[]’ syntax is used. But what would be the name of this method? The
answer is simple: [].

So we define a ‘[]’ method that would be invoked whenever we do
“some_object [ some_thing ]”. Here it is:

class Matrix
def [] (one, two, three)
puts “hello #{one}”
puts “hello #{two}”
puts “hello #{three}”
end
end

Now, it we did:

m = Matrix.new
m[100, 77, “box”]

We’d see:

hello 100
hello 77
hello box

We could use the ‘*’ syntax to allow for any number of arguments, not
just three:

class Matrix
def [] (*args)
args.each { |a|
puts “hello #{a}”
}
end
end

m = Matrix.new
m[“how”, “nice”, “to”, “have”, 13, “chocolates”]

Note that “def [] *args” is a synonym for “def [] (*args)”. Parentheses
aren’t mandatory in Ruby.