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

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

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

    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, May 6, 2008
    #1
    1. Advertising

  2. RichardOnRails

    Tim Hunter Guest

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


    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
    #2
    1. Advertising

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

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

    >
    > 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,
    thanks for your input.

    Best wishes,
    Richard
    RichardOnRails, May 7, 2008
    #3
  4. RichardOnRails

    Phrogz Guest

    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
    #4
  5. 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
    #5
  6. 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
    #6
  7. 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
    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
    RichardOnRails, May 7, 2008
    #7
  8. [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
    > 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
    >
    >
    >
    >
    >
    >
    Daniel Finnie, May 7, 2008
    #8
  9. 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 '[]'.

    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.
    --
    Posted via http://www.ruby-forum.com/.
    Albert Schlef, May 7, 2008
    #9
  10. 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
    > 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


    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
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. jstorta
    Replies:
    3
    Views:
    427
    jstorta
    Feb 20, 2006
  2. Oodini
    Replies:
    1
    Views:
    1,746
    Keith Thompson
    Sep 27, 2005
  3. S.Volkov
    Replies:
    2
    Views:
    205
    S.Volkov
    Mar 12, 2006
  4. Trans
    Replies:
    8
    Views:
    304
    Robert Klemme
    Oct 23, 2008
  5. Fab

    Subclass of subclass

    Fab, Aug 9, 2012, in forum: C++
    Replies:
    0
    Views:
    382
Loading...

Share This Page