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

Discussion in 'Ruby' started by RichardOnRails, May 6, 2008.

1. RichardOnRailsGuest

Hi,

Daniel Finnie 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.

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, May 6, 2008

2. Tim HunterGuest

RichardOnRails wrote:
> Hi,
>
> Daniel Finnie 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.
>
> 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]
>

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.

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

Tim Hunter, May 6, 2008

3. RichardOnRailsGuest

On May 6, 5:20 pm, Tim Hunter <> wrote:
> RichardOnRails wrote:
> > Hi,

>
> > Daniel Finnie 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.

>
> > 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]

>
> 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.
>
> --
> 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,

Best wishes,
Richard

RichardOnRails, May 7, 2008
4. PhrogzGuest

On May 6, 8:51 pm, RichardOnRails
<> 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

Phrogz, May 7, 2008
5. Albert SchlefGuest

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]]".
--
Posted via http://www.ruby-forum.com/.

Albert Schlef, May 7, 2008
6. RichardOnRailsGuest

On May 6, 11:07 pm, Phrogz <> wrote:
> On May 6, 8:51 pm, RichardOnRails
>
> <> 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

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>

RichardOnRails, May 7, 2008
7. RichardOnRailsGuest

On May 7, 4:06 am, Albert Schlef <> wrote:
> 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]]".
> --
> 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
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

RichardOnRails, May 7, 2008
8. Daniel FinnieGuest

[Note: parts of this message were removed to make it a legal post.]

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 <>
wrote:
>
> On May 7, 4:06 am, Albert Schlef <> wrote:
> > 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]]".
> > --
> > 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
> 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
>
>
>
>
>
>

Daniel Finnie, May 7, 2008
9. Albert SchlefGuest

Daniel Finnie was faster than me ;-) 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 '[]'.

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

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.
--
Posted via http://www.ruby-forum.com/.

Albert Schlef, May 7, 2008
10. RichardOnRailsGuest

On May 7, 12:41 pm, RichardOnRails
<> wrote:
> On May 7, 4:06 am, Albert Schlef <> wrote:
>
>
>
> > 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]]".
> > --
> > 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
> 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

Hello, again, Daniel and Albert,

You've both been very gracious and generous in educating me in the
Ruby Way (to borrow Hal Fulton'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

RichardOnRails, May 8, 2008