Problem assigning an Array object to an Array-subclass object

R

Richard Lionheart

[ 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
 
R

Richard Lionheart

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
 
A

Ara.T.Howard

[ 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
===============================================================================
 
R

Richard Lionheart

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.

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
 
A

Ara.T.Howard

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.

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.

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
===============================================================================
 
M

Mark Hubbart

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.

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.

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
 
K

Kristof Bastiaensen

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)
 
R

Robert Klemme

Richard Lionheart said:
[ 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
 
D

Dick Davies

* Kristof Bastiaensen said:
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$
 
R

Richard Lionheart

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

Richard Lionheart

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").
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
 
A

Ara.T.Howard

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

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

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
===============================================================================
 
R

Richard Lionheart

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
 
R

Richard Lionheart

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
 
R

Richard Lionheart

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
 
R

Richard Lionheart

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
 
M

Mark Sparshatt

Richard said:
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
 
R

Richard Lionheart

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
 
R

Richard Lionheart

Hi Ara,

Ara.T.Howard said:
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
 
R

Richard Lionheart

# 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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top