Problem assigning an Array object to an Array-subclass object

Discussion in 'Ruby' started by Richard Lionheart, Apr 23, 2004.

  1. [ I apologize if this is a second post. My earlier one seems to have gotten
    lost in the ethernet :) ]

    notes =<<NOTES
    I didn't like to_s' output for an Array of strings.
    Array#to_s simply concatenated them
    or inserted a comma separator.
    So I wrote my own.
    But as the last line indicates, I failed.

    Apparently, array assignment to myA1
    changed the reference from a MyArrayType object
    to an Array object.

    Two questions:
    1. How can I set myA1 to an array value
    and preserve it's type?
    2. Isn't there a "Ruby way" to set the separator
    (I thought there was some special symbol a la Perl
    but I couldn't find any in Thomas' or Fulton's
    books.)
    NOTES

    class MyArrayType < Array
    def to_s
    print "Starting MyArrayType#to_s"
    s = ""
    s.each { |x| s += "; " if s.length>0; s += x.to_s }
    s
    end
    end

    myA1 = MyArrayType.new
    myA1 = %w[x1, y1]
    puts myA1.to_s # x1,y1 .... expected x1; x2 .... MyArrayType#to_s not
    invoked




    ---
    Outgoing mail is certified Virus Free.
    Checked by AVG anti-virus system (http://www.grisoft.com).
    Version: 6.0.659 / Virus Database: 423 - Release Date: 4/17/2004
    Richard Lionheart, Apr 23, 2004
    #1
    1. Advertising

  2. Woops: "typo" in next to last line

    puts myA1.to_s # x1,y1 .... expected x1; x2 .... MyArrayType#to_s not

    should read:

    puts myA1.to_s # x1,y1 .... expected x1; y1 .... MyArrayType#to_s not
    reached




    ---
    Outgoing mail is certified Virus Free.
    Checked by AVG anti-virus system (http://www.grisoft.com).
    Version: 6.0.665 / Virus Database: 428 - Release Date: 4/21/2004
    Richard Lionheart, Apr 23, 2004
    #2
    1. Advertising

  3. Richard Lionheart

    Ara.T.Howard Guest

    On Fri, 23 Apr 2004, Richard Lionheart wrote:

    > [ I apologize if this is a second post. My earlier one seems to have gotten
    > lost in the ethernet :) ]
    >
    > notes =<<NOTES
    > I didn't like to_s' output for an Array of strings.
    > Array#to_s simply concatenated them
    > or inserted a comma separator.
    > So I wrote my own.
    > But as the last line indicates, I failed.
    >
    > Apparently, array assignment to myA1
    > changed the reference from a MyArrayType object
    > to an Array object.
    >
    > Two questions:
    > 1. How can I set myA1 to an array value
    > and preserve it's type?
    > 2. Isn't there a "Ruby way" to set the separator
    > (I thought there was some special symbol a la Perl
    > but I couldn't find any in Thomas' or Fulton's
    > books.)
    > NOTES
    >
    > class MyArrayType < Array
    > def to_s
    > print "Starting MyArrayType#to_s"
    > s = ""
    > s.each { |x| s += "; " if s.length>0; s += x.to_s }
    > s
    > end
    > end
    >
    > myA1 = MyArrayType.new
    > myA1 = %w[x1, y1]
    > puts myA1.to_s # x1,y1 .... expected x1; x2 .... MyArrayType#to_s not
    > invoked



    try this:

    class MyArrayType < Array
    def to_s
    join ';'
    end
    end

    myA1 = MyArrayType['x1', 'y1']
    puts myA1.to_s # => x1;y1
    puts myA1 # => x1\ny1


    your statement:

    > Apparently, array assignment to myA1
    > changed the reference from a MyArrayType object
    > to an Array object.


    was correct

    what you did i just like

    myA1 = MyArrayType.new
    myA1 = 'fubar'

    so, of course, your method was not called


    something else to be aware of:

    - Object#puts behaves different if passed an Array, or sub-class of Array,
    see above

    in general it can be tricky to inherit from the built-ins.
    -a
    --
    ===============================================================================
    | EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
    | PHONE :: 303.497.6469
    | ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
    | URL :: http://www.ngdc.noaa.gov/stp/
    | TRY :: for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done
    ===============================================================================
    Ara.T.Howard, Apr 23, 2004
    #3
  4. Great answers! A few more questions

    Hi Ara,

    Great answers. BTW, I had another typo in my original post, which I
    corrected below. I've got a couple more questions, but if you don't feel
    like spending any more time on this question, please ignore this post.

    > join ';'


    That's a lot better than my procedural code. Works great

    > myA1 = MyArrayType['x1', 'y1']


    (Q1) That worked, but where the heck did you come up with that? Is that
    documented somewhere? I've got The Pragmatic Programmer/Thomas and The Ruby
    Way/Fulton and didn't notice that in either work (though I certainly have
    not read every word of either one.)

    > Object#puts behaves different if passed an Array, or sub-class of Array


    In my case, I think I was passing puts the the last expression executed in
    the to_s method of an Array-subclass object. So I really wasn't passing an
    Array nor a sub-class of Array.
    (Q2) Do you agree?

    Below are several variants of code for this question.
    (Q3) My last question is how can I make the MyArrayType3 invocation work?

    MyArrayType1 uses my original code (absent the typo) and is invoked with an
    array of explicitly quoted strings as you suggest. Works great.

    MyArrayType2 uses your join expresson and is invoked with an array of
    explicitly quoted strings as you suggest. Works great.

    MyArrayType3 uses your join expresson but adds an initialize method so it
    can be invoked with an array object argument. Fails.
    (Q4) Why?

    Again, thanks for your great response.

    Regards,
    Richard


    class MyArrayType1 < Array
    def to_s
    s = ""
    self.each { |x|
    s += "; " if s.length>0
    s += x.to_s
    }
    s
    end
    end

    class MyArrayType2 < Array
    def to_s
    join '; '
    end
    end

    class MyArrayType3 < Array
    def to_s
    sOut = @s.join '; '
    end
    def intialize(aIn)
    @s = aIn
    end
    end

    myA1 = MyArrayType1['x1', 'y1']
    puts myA1.to_s # x1; y1

    myA2 = MyArrayType2['x1', 'y1']
    puts myA2.to_s # x1; y1

    a3 = %w[x1, y1]
    myA3 = MyArrayType(a3) # undefined method `MyArrayType' for main:Object
    (NoMethodError)
    puts myA3.to_s



    ---
    Outgoing mail is certified Virus Free.
    Checked by AVG anti-virus system (http://www.grisoft.com).
    Version: 6.0.665 / Virus Database: 428 - Release Date: 4/22/2004
    Richard Lionheart, Apr 24, 2004
    #4
  5. Richard Lionheart

    Ara.T.Howard Guest

    Re: Great answers! A few more questions

    On Sat, 24 Apr 2004, Richard Lionheart wrote:

    > Hi Ara,
    >
    > Great answers. BTW, I had another typo in my original post, which I
    > corrected below. I've got a couple more questions, but if you don't feel
    > like spending any more time on this question, please ignore this post.
    >
    > > join ';'

    >
    > That's a lot better than my procedural code. Works great
    >
    > > myA1 = MyArrayType['x1', 'y1']

    >
    > (Q1) That worked, but where the heck did you come up with that? Is that
    > documented somewhere? I've got The Pragmatic Programmer/Thomas and The Ruby
    > Way/Fulton and didn't notice that in either work (though I certainly have
    > not read every word of either one.)


    frankly, i find that book amazing. it got me into ruby and yet i still find
    things in it almost weekly. to answer your question, it's in

    'Built-in Classes and Methods'->'Array'->'[]'

    the html version of the book is very useful.

    note that '[]' is simply an alias for Array#new, which in turn will construct
    an Array object and call #initialize on it.


    > > Object#puts behaves different if passed an Array, or sub-class of Array

    >
    > In my case, I think I was passing puts the the last expression executed in
    > the to_s method of an Array-subclass object. So I really wasn't passing an
    > Array nor a sub-class of Array.
    > (Q2) Do you agree?


    yes. just a warning.


    > Below are several variants of code for this question.
    > (Q3) My last question is how can I make the MyArrayType3 invocation work?


    myA3 = MyArrayType(a3) # undefined method `MyArrayType' for main:Object
    ^
    ^
    ^
    ^
    3.new
    ^^^^^
    ^^^^^
    myA3 = MyArrayType3.new(a3) # undefined method `MyArrayType' for main:Object
    ^^^^^

    typo? ;-)

    perhaps something like this (un-tested)?

    class MyArrayType < Array
    def initialize(*args, &block)
    if Array === args.first
    ary = args.shift
    super
    update ary
    else
    super
    end
    end
    def to_s;join '; ';end
    end

    again, be very careful extending/inheriting builtin classes - it's powerful
    because you get so much for free, but it can come back to bite you when you
    expect it to be just __like__ to built-ins, eg:

    ma = MyArrayType['42']
    a = ['forty-two']

    ma + a # => this will be an Array!

    ma << 42.0 # => MyArrayType
    a << 42.0 # => Array

    eg. alot of the methods you inherit will not return objects of your
    specialized class, but of the parent class (Array in this case). this isn't
    too much of a problem unless you expect these object to have you new methods
    (to_s for instance)...

    an aggregate class is often safer and less frustrating to debug...

    > MyArrayType1 uses my original code (absent the typo) and is invoked with an
    > array of explicitly quoted strings as you suggest. Works great.
    >
    > MyArrayType2 uses your join expresson and is invoked with an array of
    > explicitly quoted strings as you suggest. Works great.
    >
    > MyArrayType3 uses your join expresson but adds an initialize method so it
    > can be invoked with an array object argument. Fails.
    > (Q4) Why?


    see above. code looks correct, just a typo.

    > Again, thanks for your great response.


    no worries, it's just karma - i've gotten plenty of great responses myself...

    > Regards,
    > Richard
    >
    >
    > class MyArrayType1 < Array
    > def to_s
    > s = ""
    > self.each { |x|
    > s += "; " if s.length>0
    > s += x.to_s
    > }
    > s
    > end
    > end
    >
    > class MyArrayType2 < Array
    > def to_s
    > join '; '
    > end
    > end
    >
    > class MyArrayType3 < Array
    > def to_s
    > sOut = @s.join '; '
    > end
    > def intialize(aIn)
    > @s = aIn
    > end
    > end
    >
    > myA1 = MyArrayType1['x1', 'y1']
    > puts myA1.to_s # x1; y1
    >
    > myA2 = MyArrayType2['x1', 'y1']
    > puts myA2.to_s # x1; y1
    >
    > a3 = %w[x1, y1]
    > myA3 = MyArrayType(a3) # undefined method `MyArrayType' for main:Object
    > (NoMethodError)
    > puts myA3.to_s


    cheers.

    -a
    --
    ===============================================================================
    | EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
    | PHONE :: 303.497.6469
    | ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
    | URL :: http://www.ngdc.noaa.gov/stp/
    | TRY :: for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done
    ===============================================================================
    Ara.T.Howard, Apr 24, 2004
    #5
  6. Richard Lionheart

    Mark Hubbart Guest

    Re: Great answers! A few more questions

    On Apr 23, 2004, at 9:29 PM, Richard Lionheart wrote:

    > Hi Ara,
    >
    > Great answers. BTW, I had another typo in my original post, which I
    > corrected below. I've got a couple more questions, but if you don't
    > feel
    > like spending any more time on this question, please ignore this post.
    >
    >> join ';'

    >
    > That's a lot better than my procedural code. Works great
    >
    >> myA1 = MyArrayType['x1', 'y1']

    >
    > (Q1) That worked, but where the heck did you come up with that? Is
    > that
    > documented somewhere?


    Since MyArrayType inherits from Array, you get all it's methods for
    free. One of those is Array[], which creates a new array from the
    arguments passed. Since you call it using MyArrayType rather than
    Array, it creates an instance of MyArrayType. It's documented in
    PickAxe with Array's other class methods.


    >> Object#puts behaves different if passed an Array, or sub-class of
    >> Array

    >
    > In my case, I think I was passing puts the the last expression
    > executed in
    > the to_s method of an Array-subclass object. So I really wasn't
    > passing an
    > Array nor a sub-class of Array.
    > (Q2) Do you agree?


    Correct. You were passing it a String. I think Ara was just making a
    related observation about how Kernel#puts works.

    > Below are several variants of code for this question.
    > (Q3) My last question is how can I make the MyArrayType3 invocation
    > work?


    Three reasons it won't work properly:
    - You are calling MyArrayType3(data), rather than the appropriate
    MyArrayType3[data] (note the square brackets).

    - Your initialize method is spelled wrong

    - You aren't calling MyArrayType3.new, which would call the initialize
    function. You are calling MyArrayType3() which is interpreted to be a
    method of Object; ie., Object#MyArrayType3()

    - You are assigning the array data to an instance variable, then
    joining it for to_s. What you have there is an empty MyArrayType
    instance with an instance variable that's holding an array. This will
    give you wrong behavior if you do anything with that instance other
    than call to_s: other method calls will use the non-existant internal
    array data.

    To make it work properly, you might do it like this:

    class MyArrayType3 < Array
    def initialize(ary)
    self.concat! ary
    end
    def to_s
    join "; "
    end
    end


    >
    > MyArrayType1 uses my original code (absent the typo) and is invoked
    > with an
    > array of explicitly quoted strings as you suggest. Works great.
    >
    > MyArrayType2 uses your join expresson and is invoked with an array of
    > explicitly quoted strings as you suggest. Works great.
    >
    > MyArrayType3 uses your join expresson but adds an initialize method so
    > it
    > can be invoked with an array object argument. Fails.
    > (Q4) Why?
    >
    > Again, thanks for your great response.
    >
    > Regards,
    > Richard
    >
    >
    > class MyArrayType1 < Array
    > def to_s
    > s = ""
    > self.each { |x|
    > s += "; " if s.length>0
    > s += x.to_s
    > }
    > s
    > end
    > end
    >
    > class MyArrayType2 < Array
    > def to_s
    > join '; '
    > end
    > end
    >
    > class MyArrayType3 < Array
    > def to_s
    > sOut = @s.join '; '
    > end
    > def intialize(aIn)
    > @s = aIn
    > end
    > end
    >
    > myA1 = MyArrayType1['x1', 'y1']
    > puts myA1.to_s # x1; y1
    >
    > myA2 = MyArrayType2['x1', 'y1']
    > puts myA2.to_s # x1; y1
    >
    > a3 = %w[x1, y1]
    > myA3 = MyArrayType(a3) # undefined method `MyArrayType' for
    > main:Object
    > (NoMethodError)
    > puts myA3.to_s
    >
    >
    >
    > ---
    > Outgoing mail is certified Virus Free.
    > Checked by AVG anti-virus system (http://www.grisoft.com).
    > Version: 6.0.665 / Virus Database: 428 - Release Date: 4/22/2004
    >
    >
    >
    Mark Hubbart, Apr 24, 2004
    #6
  7. On Fri, 23 Apr 2004 11:15:16 -0400, Richard Lionheart wrote:

    > notes =<<NOTES
    > I didn't like to_s' output for an Array of strings.
    > Array#to_s simply concatenated them
    > or inserted a comma separator.
    > So I wrote my own.
    >
    > class MyArrayType < Array
    > def to_s
    > print "Starting MyArrayType#to_s"
    > s = ""
    > s.each { |x| s += "; " if s.length>0; s += x.to_s }
    > s
    > end
    > end
    >
    > myA1 = MyArrayType.new
    > myA1 = %w[x1, y1]
    > puts myA1.to_s # x1,y1 .... expected x1; x2 .... MyArrayType#to_s not
    > invoked
    >


    Hi,
    if all you want to do is change the to_s method, why not
    just add it to Array?

    class Array
    def my_to_s
    join("; ")
    end
    end

    a = %w[x1 y1]
    puts a.my_to_s

    #note (you could even replace the to_s method)
    Kristof Bastiaensen, Apr 24, 2004
    #7
  8. "Richard Lionheart" <> schrieb im Newsbeitrag
    news:...
    > [ I apologize if this is a second post. My earlier one seems to have

    gotten
    > lost in the ethernet :) ]
    >
    > notes =<<NOTES
    > I didn't like to_s' output for an Array of strings.
    > Array#to_s simply concatenated them
    > or inserted a comma separator.
    > So I wrote my own.
    > But as the last line indicates, I failed.
    >
    > Apparently, array assignment to myA1
    > changed the reference from a MyArrayType object
    > to an Array object.
    >
    > Two questions:
    > 1. How can I set myA1 to an array value
    > and preserve it's type?
    > 2. Isn't there a "Ruby way" to set the separator
    > (I thought there was some special symbol a la Perl
    > but I couldn't find any in Thomas' or Fulton's
    > books.)
    > NOTES
    >
    > class MyArrayType < Array
    > def to_s
    > print "Starting MyArrayType#to_s"
    > s = ""
    > s.each { |x| s += "; " if s.length>0; s += x.to_s }
    > s
    > end
    > end
    >
    > myA1 = MyArrayType.new
    > myA1 = %w[x1, y1]


    There seems to be a misunderstanding on your side about variables, objects
    and types. Variables do not have a type, they just hold references to any
    object. So your code does not declare a variable "myA1" of type
    MyArrayType, it merely first assigns the result of MyArrayType.new (a new
    instance of class MyArrayType) and then assigns the result of %w[x1, y1] to
    myA1, loosing the reference to the MyArrayType instance.

    Simply use Array#join:

    irb(main):014:0> %w[x1, x2].join( '; ' )
    => "x1,; x2"

    Note: the ',' is part of the first word. You probably wanted

    irb(main):015:0> %w[x1 x2].join( '; ' )
    => "x1; x2"

    Regards

    robert
    Robert Klemme, Apr 24, 2004
    #8
  9. Richard Lionheart

    Dick Davies Guest

    * Kristof Bastiaensen <> [0404 10:04]:
    > On Fri, 23 Apr 2004 11:15:16 -0400, Richard Lionheart wrote:
    >
    > > notes =<<NOTES
    > > I didn't like to_s' output for an Array of strings.
    > > Array#to_s simply concatenated them
    > > or inserted a comma separator.
    > > So I wrote my own.
    > >
    > > class MyArrayType < Array
    > > def to_s
    > > print "Starting MyArrayType#to_s"
    > > s = ""
    > > s.each { |x| s += "; " if s.length>0; s += x.to_s }
    > > s
    > > end
    > > end
    > >
    > > myA1 = MyArrayType.new
    > > myA1 = %w[x1, y1]
    > > puts myA1.to_s # x1,y1 .... expected x1; x2 .... MyArrayType#to_s not
    > > invoked
    > >

    >
    > Hi,
    > if all you want to do is change the to_s method, why not
    > just add it to Array?
    >
    > class Array
    > def my_to_s
    > join("; ")
    > end
    > end
    >
    > a = %w[x1 y1]
    > puts a.my_to_s


    Trouble is some other Array using method may expect tho old behaviour,
    but you can do this on a per instance basis:

    0rasputin@lb:rasputin$ ruby instancedef.rb
    before override: a = foobarack, b = eenymeenymineymo
    after override: a = foo:bar:ack, b = eenymeenymineymo
    0rasputin@lb:rasputin$ cat instancedef.rb
    a = []
    b = []

    a = %w( foo bar ack)
    b = %w(eeny meeny miney mo)

    puts "before override: a = #{a.to_s}, b = #{b.to_s}"

    def a.to_s
    self.join(':')
    end

    puts "after override: a = #{a.to_s}, b = #{b.to_s}"
    0rasputin@lb:rasputin$

    --
    Is it possible that software is not like anything else, that it is
    meant to be discarded: that the whole point is to always see it as a
    soap bubble?
    Rasputin :: Jack of All Trades - Master of Nuns
    Dick Davies, Apr 24, 2004
    #9
  10. Re: Great answers! A few more questions

    Hi Mark,

    Okay, following your advice, I got this working perfectly:

    class MyArrayType3 < Array
    def to_s
    join '; '
    end
    def initalize(aIn)
    self.concat! aIn
    end
    end

    a3 = %w[x3 y3]
    myA3 = MyArrayType3[a3]
    puts myA3.to_s # ==> x3; y3

    In testing it, I had puts statements in both methods and found that
    MyArrayType3#intialize is never called in this invocation.

    That's consistent with what you told me: MyArrayType3.new would invoke
    initialize.

    That begs the question: In what circumstance would MyArrayType3#intialize
    be invoked.

    I apologize for the several typos I had in my previous attempt(s). Thanks
    for your excellent explanations. They're much appreciated.

    Regards,
    Richard

    P.S. I mistakenly replied to you directly rather than posting back on this
    thread. Please excuse me.


    ---
    Outgoing mail is certified Virus Free.
    Checked by AVG anti-virus system (http://www.grisoft.com).
    Version: 6.0.665 / Virus Database: 428 - Release Date: 4/22/2004
    Richard Lionheart, Apr 24, 2004
    #10
  11. Re: Great answers! A few more questions

    Hi Ara,

    Thanks for your additional info.

    > myA3 = MyArrayType3.new(a3) # undefined method `MyArrayType' for

    main:Object
    > ^^^^^
    >
    > typo? ;-)


    Yes, indeed! I apologize for mixing up sloppy coding with my ignorant
    coding. IMHO, the latter is exusable, but the former is not.

    > perhaps something like this (un-tested)?


    No problem!! I'll test it :)

    > class MyArrayType < Array
    > def initialize(*args, &block)
    > if Array === args.first
    > ary = args.shift
    > super
    > update ary
    > else
    > super
    > end
    > end
    > def to_s;join '; ';end
    > end


    a4 = %w[x4 y4]
    myA4 = MyArrayType.new(a4) # ==> undefined method `update' for ["x4",
    "y4"]:MyArrayType (NoMethodError)
    puts myA4b.to_s # wasn't reached

    The Thomas/Hunt book says 'update' is a method in Hash and CGI::Session,
    but not in Array.

    > again, be very careful extending/inheriting builtin classes ...


    Thanks. I've already experienced some of the ones you mentioned in the
    course of this exercise, so I feel up-to-speed on the matter.

    Your 'initialize' method did present me with a few things I have to ask you
    about, particularly whether my understanding/guess is correct:

    > def initialize(*args, &block)

    Could (must?) be invoked as MyArrayType['x','y']{|i| dostuff(i)}
    so that block(element) could be executed
    which in turn would invoke dostuff(elemement)
    for selected elements in the array(s)
    in args.

    > if Array === args.first

    My guess is that this statement is equivalent to "if 'Array' is the type of
    the first item in the 'args' array." That doesn't comport with "Programming
    Ruby", Thomas et al, 2001, page 283, which seems to indicate "element by
    element comparison of two arrays." But I can't see how you could intend to
    compare Array's elements, because I don't think Array HAS any elements when
    'initialize' is invoked.

    > ary = args.shift

    Equivalent to "ary = args[0]" in this instance, since 'args' is not modified
    (no "!" after "shift").

    > super

    Invokes Array#initialze with the original arguments that 'initialize' got,
    but I don't see what this accomplishes.

    > an aggregate class is often safer and less frustrating to debug...

    I assume you're talking about using a Module and including it into a class
    or classes.

    As before, I appreciate your tutoring.

    Regards,
    Richard



    ---
    Outgoing mail is certified Virus Free.
    Checked by AVG anti-virus system (http://www.grisoft.com).
    Version: 6.0.665 / Virus Database: 428 - Release Date: 4/22/2004
    Richard Lionheart, Apr 25, 2004
    #11
  12. Richard Lionheart

    Ara.T.Howard Guest

    Re: Great answers! A few more questions

    On Sat, 24 Apr 2004, Richard Lionheart wrote:

    > The Thomas/Hunt book says 'update' is a method in Hash and CGI::Session, but
    > not in Array.


    sorry - i meant Array#replace

    > > def initialize(*args, &block)

    > Could (must?) be invoked as MyArrayType['x','y']{|i| dostuff(i)} so that
    > block(element) could be executed which in turn would invoke
    > dostuff(elemement) for selected elements in the array(s) in args.


    ~ > cat a.rb
    class A
    def initialize(*args, &block)
    puts '---'
    p "args <#{ args.inspect }>"
    block.call if block
    end
    end

    A.new
    A.new{ p 'forty-two'}
    A.new 42
    A.new(42){ p 'forty-two'}


    ~ > ruby a.rb
    ---
    "args <[]>"
    ---
    "args <[]>"
    "forty-two"
    ---
    "args <[42]>"
    ---
    "args <[42]>"
    "forty-two"


    if you look at the docs for Array#new you'll see it comes in three of the
    flavors above, since i needed the same method signature as Array#new this was
    a fast dirty way to do it without really needing to know the exact methed
    signature. if you program C this is about like saying

    int
    method()
    {
    }

    or in C++

    int
    method(...)
    {
    }

    execpt it's __much__ easier to get at whatever args the methods has been
    called with!

    > > if Array === args.first

    > My guess is that this statement is equivalent to "if 'Array' is the type of
    > the first item in the 'args' array." That doesn't comport with "Programming
    > Ruby", Thomas et al, 2001, page 283, which seems to indicate "element by
    > element comparison of two arrays." But I can't see how you could intend to
    > compare Array's elements, because I don't think Array HAS any elements when
    > 'initialize' is invoked.


    note the '===' vs '=='. the method being invoked is the __class__ method
    '==='. in this case Array inherits this method from Class - one of it's
    parents. so check out the class method of Class named '==='. ;-)

    eg.

    ~ > cat b.rb
    array = [42]
    hash = {4 => 2}
    file = open __FILE__

    [array, hash, file].each do |obj|
    [Array, Hash, File].each do |klass|
    printf "<%s> %s <%s>\n", obj.inspect, (klass === obj ? "is a" : "is not a"), klass
    end
    end

    ~ > ruby b.rb
    <[42]> is a <Array>
    <[42]> is not a <Hash>
    <[42]> is not a <File>
    <{4=>2}> is not a <Array>
    <{4=>2}> is a <Hash>
    <{4=>2}> is not a <File>
    <#<File:b.rb>> is not a <Array>
    <#<File:b.rb>> is not a <Hash>
    <#<File:b.rb>> is a <File>


    case uses '===' by default, so you can also

    ~ > cat c.rb
    array = [42]
    hash = {4 => 2}
    file = open __FILE__

    [array, hash, file].each do |obj|
    case obj
    when Array
    puts "<#{ obj.inspect }> is a Array"
    when Hash
    puts "<#{ obj.inspect }> is a Hash"
    when File
    puts "<#{ obj.inspect }> is a File"
    end
    end

    ~ > ruby c.rb
    <[42]> is a Array
    <{4=>2}> is a Hash
    <#<File:c.rb>> is a File


    this is a bit more rubyish i think - but both peices of code are asking if an
    object is of a certain __type__


    > > ary = args.shift

    > Equivalent to "ary = args[0]" in this instance, since 'args' is not modified
    > (no "!" after "shift").


    you can always play and find out:

    ~ > irb
    irb(main):001:0> a = [0,1,2]
    => [0, 1, 2]
    irb(main):002:0> a.shift
    => 0
    irb(main):003:0> a
    => [1, 2]

    shift is a method that DOES modify it's reciever. not all such methods end in
    '!', such as Array#delete. there is no hard and fast rule about this in ruby,
    but in general one can say that methods for which it is non-intuitive that the
    method would modify the reciever have a bang, and methods for which there are
    two versions (modifying and non-modifying) have a bang version. essentially
    it's up to the designer of the class to decide which methods are 'dangerous'.
    eg.

    ~ > irb
    irb(main):001:0> a = [0,1,2]
    => [0, 1, 2]
    irb(main):002:0> a.map{|elem| elem ** 2}
    => [0, 1, 4]
    irb(main):003:0> a
    => [0, 1, 2]
    irb(main):004:0> a.map!{|elem| elem ** 2}
    => [0, 1, 4]
    irb(main):005:0> a
    => [0, 1, 4]
    irb(main):006:0> a.delete 0
    => 0
    irb(main):007:0> a
    => [1, 4]


    > > super

    > Invokes Array#initialze with the original arguments that 'initialize' got,
    > but I don't see what this accomplishes.


    that's sort of the point - who knows what Array#initialize does, but we are
    trying to 'be an Array' so we had better do it. we could look into the
    sources for Array and find out, or let ruby do it for us. consider:

    ~ > cat d.rb
    class Base
    def initialize val
    @val = val
    end
    def meth
    p(@val + 2)
    end
    end

    class Derived0 < Base
    def initialize(*args, &block)
    super
    end
    end
    class Derived1 < Base
    def initialize(*args, &block)
    # nothing
    end
    end

    d0 = Derived0.new 40
    d1 = Derived1.new 40

    d0.meth
    d1.meth

    ~ > ruby d.rb
    42
    d.rb:7:in `meth': undefined method `+' for nil:NilClass (NoMethodError)
    from d.rb:26


    > > an aggregate class is often safer and less frustrating to debug...

    > I assume you're talking about using a Module and including it into a class
    > or classes.
    >
    > As before, I appreciate your tutoring.


    well - don't take my word for it, play around and read the group - lot's of
    way to skin a cat. ;-)

    cheers.

    -a
    --
    ===============================================================================
    | EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
    | PHONE :: 303.497.6469
    | ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
    | URL :: http://www.ngdc.noaa.gov/stp/
    | TRY :: for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done
    ===============================================================================
    Ara.T.Howard, Apr 25, 2004
    #12
  13. Thanks, Kristof

    > class Array
    > def my_to_s
    > join("; ")
    > end
    > end
    >
    > a = %w[x1 y1]
    > puts a.my_to_s
    >
    > #note (you could even replace the to_s method)


    I realize now I could just append
    ..join("; ").to_s
    to my array and not bother with subclassing Array, etc.

    Regards,
    Richard


    ---
    Outgoing mail is certified Virus Free.
    Checked by AVG anti-virus system (http://www.grisoft.com).
    Version: 6.0.665 / Virus Database: 428 - Release Date: 4/26/2004
    Richard Lionheart, Apr 28, 2004
    #13
  14. Hi Dick,

    Gotcha! Applying your ideas, modified slightly I believe, I tested:

    # F:\_Projects_Current\_Projects_Ruby\P001f-Arrays#3\MyArr3.rb

    a = []
    b = []

    a = %w( foo bar ack)
    b = %w(eeny meeny miney mo)
    puts "Before override: a = #{a.to_s}, b = #{b.to_s}"

    class Array
    def to_s
    join("; ")
    end
    end

    a = %w( foo bar ack)
    b = %w(eeny meeny miney mo)
    puts "After override: a = #{a.to_s}, b = #{b.to_s}"

    ####### Got results:

    >ruby MyArr3.rb Arg1 Arg2

    Before override: a = foobarack, b = eenymeenymineymo
    After override: a = foo; bar; ack, b = eeny; meeny; miney; mo
    >Exit code: 0


    #####

    Neat stuff!

    Thanks,
    Richard


    ---
    Outgoing mail is certified Virus Free.
    Checked by AVG anti-virus system (http://www.grisoft.com).
    Version: 6.0.665 / Virus Database: 428 - Release Date: 4/26/2004
    Richard Lionheart, Apr 28, 2004
    #14
  15. Hi Robert,

    Thanks for your response.

    > There seems to be a misunderstanding on your side about variables, objects
    > and types. Variables do not have a type, they just hold references to any
    > object. So your code does not declare a variable "myA1" of type
    > MyArrayType, it merely first assigns the result of MyArrayType.new (a new
    > instance of class MyArrayType) and then assigns the result of %w[x1, y1]

    to
    > myA1, loosing the reference to the MyArrayType instance.


    Good point. I've got it now!

    > Simply use Array#join:
    >
    > irb(main):014:0> %w[x1, x2].join( '; ' )
    > => "x1,; x2"


    Yes, much, much better than my original idea

    > Note: the ',' is part of the first word. You probably wanted
    >
    > irb(main):015:0> %w[x1 x2].join( '; ' )
    > => "x1; x2"


    Yes, that's what I wanted and was surprised when the comma followed "x1" in
    the output. So I deleted the comma and added extra spaces just to confirm
    that a set of one or more spaces was interpreted as a separator in Ruby's []
    notation.

    Again, thanks for your help,
    Richard


    ---
    Outgoing mail is certified Virus Free.
    Checked by AVG anti-virus system (http://www.grisoft.com).
    Version: 6.0.665 / Virus Database: 428 - Release Date: 4/26/2004
    Richard Lionheart, Apr 28, 2004
    #15
  16. Last minute details

    Hi Dick,

    I think I'm on top of the subtleties involved with my question:

    1. I dropped the initial assignments to a and b, because no matter what
    they refer to (empty array, string, whatever), they'll be assigned new
    references by the subsequent assignments. I confirmed this by adding a puts

    2. I initially dropped the "my_" prefix to to_s, since it looked more
    elegant (to me, at least). But when testing, I found now way to undo that
    change to Array so that unexpected results would probably obtain in extended
    use of my revised approach.

    Here's my last word on the subject:

    a = %w( foo bar ack)
    puts "After assignment \"a = %w( foo bar ack)\", a.class.to_s =
    #{a.class.to_s}"
    b = %w(eeny meeny miney mo)
    puts "Before override: a = #{a.to_s}, b = #{b.to_s}"

    class Array # Override to_s
    def my_to_s
    join("; ")
    end
    end

    puts "After override: a = #{a.my_to_s}, b = #{b.my_to_s}"

    results =<<RESULTS
    After assignment "a = %w( foo bar ack)", a.class.to_s = Array
    Before override: a = foobarack, b = eenymeenymineymo
    After override: a = foo; bar; ack, b = eeny; meeny; miney; mo
    RESULTS

    Again, many thanks for your advice,
    Richard


    ---
    Outgoing mail is certified Virus Free.
    Checked by AVG anti-virus system (http://www.grisoft.com).
    Version: 6.0.665 / Virus Database: 428 - Release Date: 4/26/2004
    Richard Lionheart, Apr 28, 2004
    #16
  17. Re: Last minute details

    Richard Lionheart wrote:

    >Hi Dick,
    >
    >I think I'm on top of the subtleties involved with my question:
    >
    >1. I dropped the initial assignments to a and b, because no matter what
    >they refer to (empty array, string, whatever), they'll be assigned new
    >references by the subsequent assignments. I confirmed this by adding a puts
    >
    >2. I initially dropped the "my_" prefix to to_s, since it looked more
    >elegant (to me, at least). But when testing, I found now way to undo that
    >change to Array so that unexpected results would probably obtain in extended
    >use of my revised approach.
    >
    >
    >

    if you want to save the old version of to_s you can use the alias method

    class Array # Override to_s
    alias old_to_s to_s
    def to_s
    join("; ")
    end
    end

    then you can access the old to_s behaviour by using the old_to_s method.
    Or if you want to change to_s back to it's default behaviour you can use

    class Array
    alias to_s old_to_s
    end

    i.e.

    a = %w( foo bar ack)
    puts "After assignment \"a = %w( foo bar ack)\", a.class.to_s =
    #{a.class.to_s}"
    b = %w(eeny meeny miney mo)
    puts "Before override: a = #{a.to_s}, b = #{b.to_s}"

    class Array # Override to_s
    alias old_to_s to_s
    def to_s
    join("; ")
    end
    end

    puts "After override: a = #{a.to_s}, b = #{b.to_s}"

    class Array
    alias to_s old_to_s
    end

    puts "After re-override: a = #{a.to_s}, b = #{b.to_s}"

    results=<<RESULTS
    After assignment "a = %w( foo bar ack)", a.class.to_s =
    Array
    Before override: a = foobarack, b = eenymeenymineymo
    After override: a = foo; bar; ack, b = eeny; meeny; miney; mo
    After re-override: a = foobarack, b = eenymeenymineymo
    RESULTS

    --
    Mark Sparshatt
    Mark Sparshatt, Apr 28, 2004
    #17
  18. Re: Last minute details

    Hi Mark,

    Thanks. You must be a mind-reader :) That's exactly the kind of thing I
    wanted to do. I tried to "undefine" the to_s modification by adding the
    following after the invocation of the modified Array:

    class Array
    def to_s
    self.to_s
    end
    end

    Of course, that netted me "stack level too deep (SystemStackError)"

    Again, I much appreciate your advice. It was especially generous of you to
    post your complete solution because I might not have known how to apply your
    suggestion. (In this case, happily, I understood your suggestion
    immediately.)

    Regards,
    Richard

    "Mark Sparshatt" <> wrote in message
    news:...
    > Richard Lionheart wrote:
    >
    > if you want to save the old version of to_s you can use the alias method
    >
    > class Array # Override to_s
    > alias old_to_s to_s
    > def to_s
    > join("; ")
    > end
    > end
    >
    > then you can access the old to_s behaviour by using the old_to_s method.
    > Or if you want to change to_s back to it's default behaviour you can use
    >
    > class Array
    > alias to_s old_to_s
    > end
    >
    > i.e.
    >
    > a = %w( foo bar ack)
    > puts "After assignment \"a = %w( foo bar ack)\", a.class.to_s =
    > #{a.class.to_s}"
    > b = %w(eeny meeny miney mo)
    > puts "Before override: a = #{a.to_s}, b = #{b.to_s}"
    >
    > class Array # Override to_s
    > alias old_to_s to_s
    > def to_s
    > join("; ")
    > end
    > end
    >
    > puts "After override: a = #{a.to_s}, b = #{b.to_s}"
    >
    > class Array
    > alias to_s old_to_s
    > end
    >
    > puts "After re-override: a = #{a.to_s}, b = #{b.to_s}"
    >
    > results=<<RESULTS
    > After assignment "a = %w( foo bar ack)", a.class.to_s =
    > Array
    > Before override: a = foobarack, b = eenymeenymineymo
    > After override: a = foo; bar; ack, b = eeny; meeny; miney; mo
    > After re-override: a = foobarack, b = eenymeenymineymo
    > RESULTS
    >
    > --
    > Mark Sparshatt
    >
    >
    >
    >



    ---
    Outgoing mail is certified Virus Free.
    Checked by AVG anti-virus system (http://www.grisoft.com).
    Version: 6.0.665 / Virus Database: 428 - Release Date: 4/26/2004
    Richard Lionheart, Apr 28, 2004
    #18
  19. Re: Great answers! A few more questions

    Hi Ara,

    "Ara.T.Howard" <> wrote in message
    news:p...
    > On Sat, 24 Apr 2004, Richard Lionheart wrote:
    >
    > > The Thomas/Hunt book says 'update' is a method in Hash and CGI::Session,

    but
    > > not in Array.

    >
    > sorry - i meant Array#replace


    I successfully used your previous suggestion after fixing it with 'replace'
    as you suggest.

    > ~ > cat a.rb


    Thanks for all these examples. I'm going to go through them one-by-one in a
    little while. Before I received them, I'd pretty much internalized all the
    suggestions I've received in this thread and put together a number of
    different usages of Array, documenting each one to point out the nuances
    that seem important to me. Your recent examples will give me a bunch more!

    Which leads to a question about the first one I started with, shown below.
    In my comments, I tried to indicate how the derived array got initialized,
    claiming it "uses the Array#[](a) method with an Array argument ." That
    turns out not to be correct, because MyArrayType1#[](a) did not get
    invoked.

    So what method in Array does get invoked? Or maybe it's a method in Array's
    parent, Class.

    Except for that, do my other comments correct?

    Thanks again for your superb Ruby guidance. I'll soon be able to figure out
    these kinds of questions for myself, but I haven't gotten there yet.

    Regards,
    Richard




    ---
    Outgoing mail is certified Virus Free.
    Checked by AVG anti-virus system (http://www.grisoft.com).
    Version: 6.0.665 / Virus Database: 428 - Release Date: 4/30/2004
    Richard Lionheart, May 2, 2004
    #19
  20. The code I forgot to include

    # My idea of how to convert an array of strings to a string with a specified
    delimiter
    class MyArrayType1 < Array
    def [](a)
    puts "Entering MyArrayType1#[](a) -- calling parent"
    super a
    end
    def to_s
    s = ""
    self.each { |x|
    s += "; " if s.length>0
    s += x.to_s
    }
    s
    end
    end

    myA1 = MyArrayType1['x1', 'y1'] # uses the Array#[](a) method with an Array
    argument and sets
    # myA1 to a reference to a new object of
    type MyArrayType1
    # which is an Array of two strings, 'x1'
    and 'y1'
    # with an over-ridden 'to_s' method
    puts "myA1 = MyArrayType1['x1', 'y1']; puts myA1.to_s ==> " + myA1.to_s #
    ==> x1; y1


    ---
    Outgoing mail is certified Virus Free.
    Checked by AVG anti-virus system (http://www.grisoft.com).
    Version: 6.0.665 / Virus Database: 428 - Release Date: 4/30/2004
    Richard Lionheart, May 2, 2004
    #20
    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. Richard A. DeVenezia
    Replies:
    2
    Views:
    598
    Andrew Thompson
    Jan 8, 2004
  2. jstorta
    Replies:
    3
    Views:
    441
    jstorta
    Feb 20, 2006
  3. S.Volkov
    Replies:
    2
    Views:
    215
    S.Volkov
    Mar 12, 2006
  4. Trans
    Replies:
    8
    Views:
    320
    Robert Klemme
    Oct 23, 2008
  5. Fab

    Subclass of subclass

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

Share This Page