Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > Uniform vector class, inheriting from Array: How to make sure thatmethods return a Vector and not an Array?

Reply
Thread Tools

Uniform vector class, inheriting from Array: How to make sure thatmethods return a Vector and not an Array?

 
 
Thomas
Guest
Posts: n/a
 
      05-22-2005
Hi folks,

I recently tried to implement a uniform vector class being defined as a
vector the elements of which all comply to some kind of common interface
or are a subclass of some prototype class.

My first naive approach was to inherit from Array and to overwrite some
methods to make sure that new elements are ok. This works fine for
methods like []=, <<, or unshift but when implementing + I ran into the
following problem: Array#+ returns an Array and not a Vector which is
why I have to create a new Vector from the result of Array#+. As Array#+
already creates a new Array, this probably isn't very efficient and I
don't like the idea of having to do this for all possible methods that
return an Array. I was hoping to be able to somehow limit my
modifications of Array to a few essential methods.

So, the question is: Does somebody know a way to make sure that methods
returning an Array (+, &, -, * etc.) always return a Vector without
having to redefine each of them?

Please find my current toy implementation down below.

Cheers,
Thomas.


class Vector < Array
def []=(pos, val)
check_values([val])
super
end

def +(values)
check_values(values)
Vector.new(super)
end

def <<(*values)
check_values(values)
super
end

def unshift(*values)
check_values(values)
super
end

def check_values(values)
unless defined?(@protoclass)
prototype = self[0] || values[0]
@protoclass = prototype.class
end
values.each do |e|
unless e.kind_of?(@protoclass)
raise TypeError, "Expected #{@protoclass} but got
#{e.class}", caller[2..-1]
end
end
end
end

if __FILE__ == $0
v = Vector.new
# these are okay
v << 1
v[2] = 2
p v + [1,2,3]
v += [1,2,3]
v.unshift(3)

# but this should throw an error (that's what this is all about)
v << "a"
end





 
Reply With Quote
 
 
 
 
Brian Schröder
Guest
Posts: n/a
 
      05-22-2005
On 22/05/05, Thomas <(E-Mail Removed)> wrote:
> Hi folks,
>=20
> I recently tried to implement a uniform vector class being defined as a
> vector the elements of which all comply to some kind of common interface
> or are a subclass of some prototype class.
>=20
> My first naive approach was to inherit from Array and to overwrite some
> methods to make sure that new elements are ok. This works fine for
> methods like []=3D, <<, or unshift but when implementing + I ran into the
> following problem: Array#+ returns an Array and not a Vector which is
> why I have to create a new Vector from the result of Array#+. As Array#+
> already creates a new Array, this probably isn't very efficient and I
> don't like the idea of having to do this for all possible methods that
> return an Array. I was hoping to be able to somehow limit my
> modifications of Array to a few essential methods.
>=20
> So, the question is: Does somebody know a way to make sure that methods
> returning an Array (+, &, -, * etc.) always return a Vector without
> having to redefine each of them?
>=20
> Please find my current toy implementation down below.
>=20


Hello Thomas,

I'm quite shure you don't need to do what you are doing, because this
violates duck typing, but if you really want to you can use something
like the below.

Here I don't inherit from an array, but forward to an array, which
often is the simpler approach.

Please rethink your design. If you really want to do this for speed,
you could implement a simple C extension that includes a numeric
array, or use the narray class. Otherwise just don't put anything
wrong into the array, and everything will work out fine. And if
someone wants to put something into the array that quacks like a duck
but is no duck, also everything will continue to work.

best regards,

Brian

class Vector
def initialize(*args)
@numbers =3D args
check_values(args)
end

def method_missing(method, *args, &block)
result =3D @numbers.send(method, *args, &block)
if result.is_a?Array
Vector.new(*result) =20
else
result
end
end

private
def check_values(values)
return true if @numbers.empty? and values.empty?
unless defined?(@protoclass)
prototype =3D @numbers[0] || values[0]
@protoclass =3D prototype.class
end
values.each do |e|
unless e.kind_of?(@protoclass)
=09raise TypeError, "Expected #{@protoclass} but got #{e.class}", caller[2.=
-1]
end
end
end
end

if __FILE__ =3D=3D $0
v =3D Vector.new
# these are okay
p v
v << 1
p v
v[1] =3D 2
p v
p (v + [1,2,3])
v +=3D [1,2,3]
p v
v.unshift(3)
p v

# but this should throw an error (that's what this is all about)
v << "a"
p v
end


--=20
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/


 
Reply With Quote
 
 
 
 
gabriele renzi
Guest
Posts: n/a
 
      05-22-2005
Brian Schröder ha scritto:
<snip>
>
> I'm quite shure you don't need to do what you are doing, because this
> violates duck typing, but if you really want to you can use something
> like the below.
>
> Here I don't inherit from an array, but forward to an array, which
> often is the simpler approach.



this makes me think: the delegation approach is usually simpler.
But is this because of an underlying fault of the inheritance mechanic
or is it the obvious more simple thing?
I've often thought that there must be a better way of handling this
"generative" methods like "+" (but, say, a polymorphic #map could be the
same) which make subclassing harder, has someone haver thought the same?
Is there something in other languages that allow to overcome this kind
of limitations?
 
Reply With Quote
 
Thomas
Guest
Posts: n/a
 
      05-23-2005
> I'm quite shure you don't need to do what you are doing, because this
> violates duck typing


Thank you very much for pointing out how to use method_missing here.

BTW I don't think that it "violates" duck typing. The idea is to have a
collection type class with "entrance rules" that can guarantee that
every single element complies with some sort of standard/interface/rule
set/constraints/contracts ... Using kind_of? is only one possible
constraint here. Another one could be respond_to?(:quack). On the long
run, I would like it to look/feel like this:

class QuackQuack < UniformArray
add_contstraint lambda {|e| e.respond_to?(:quack)}
end

class ArmyOfDucks < UniformArray
add_contstraint lambda {|e| e.kind_of?(Duck)}
end

class FlockOfDucks < UniformArray
add_contstraint lambda {|e| e.kind_of?(Duck)}
permit nil # There may be nil values
allow :nested # The array may be nested
end

d = UniformArray.new
d.add_contstraint lambda {|e| e.kind_of?(Numeric) and e >= 0 and e <= 10}
d.add_handler lambda {|e| e > 10 ? 10 : 0}

etc.

I don't think this is incompatible with duck typing. It's just some sort
of specialized collection type of class.

Cheers,
Thomas.



 
Reply With Quote
 
Mark Hubbart
Guest
Posts: n/a
 
      05-23-2005
On 5/23/05, Thomas <(E-Mail Removed)> wrote:
> > I'm quite shure you don't need to do what you are doing, because this
> > violates duck typing

>=20
> Thank you very much for pointing out how to use method_missing here.
>=20
> BTW I don't think that it "violates" duck typing. The idea is to have a
> collection type class with "entrance rules" that can guarantee that
> every single element complies with some sort of standard/interface/rule
> set/constraints/contracts ... Using kind_of? is only one possible
> constraint here. Another one could be respond_to?(:quack). On the long
> run, I would like it to look/feel like this:
>=20
> class QuackQuack < UniformArray
> add_contstraint lambda {|e| e.respond_to?(:quack)}
> end
>=20
> class ArmyOfDucks < UniformArray
> add_contstraint lambda {|e| e.kind_of?(Duck)}
> end
>=20
> class FlockOfDucks < UniformArray
> add_contstraint lambda {|e| e.kind_of?(Duck)}
> permit nil # There may be nil values
> allow :nested # The array may be nested
> end
>=20
> d =3D UniformArray.new
> d.add_contstraint lambda {|e| e.kind_of?(Numeric) and e >=3D 0 and e <=3D=

10}
> d.add_handler lambda {|e| e > 10 ? 10 : 0}
>=20
> etc.
>=20
> I don't think this is incompatible with duck typing. It's just some sort
> of specialized collection type of class.


Use of a "uniform" array would seem to preclude duck-typing. Even if
you use respond_to? instead of kind_of?, it wouldn't catch objects
that use method_missing to handle messages. For example:

class Wrapper
def initialize(obj)
@obj =3D obj
end
def method_missing(*args, &block)
@obj.__send__ *args, &block
end
end

If you use this class to wrap an object, code that relies entirely on
duck typing will still be able to use the object, even though it won't
respond_to? much of anything, and it won't be kind_of? the class you
want.

On the other hand, if you call it ConstrainedArray and focus on
constraints rather than types, it could end up being quite useful...

cheers,
Mark


 
Reply With Quote
 
Douglas Livingstone
Guest
Posts: n/a
 
      05-23-2005
On 5/22/05, gabriele renzi <(E-Mail Removed)> wrote:
> this makes me think: the delegation approach is usually simpler.
> But is this because of an underlying fault of the inheritance mechanic
> or is it the obvious more simple thing?


I think you've got it. Classes which have methods which return
instances of their own class should, once extended, return instances
of that extended class.

I'm not sure how it would work, something like return self.new.

Douglas


 
Reply With Quote
 
Jacob Fugal
Guest
Posts: n/a
 
      05-23-2005
On 5/23/05, Mark Hubbart <(E-Mail Removed)> wrote:
> Use of a "uniform" array would seem to preclude duck-typing. Even if
> you use respond_to? instead of kind_of?, it wouldn't catch objects
> that use method_missing to handle messages. For example:
>=20
> class Wrapper
> def initialize(obj)
> @obj =3D obj
> end
> def method_missing(*args, &block)
> @obj.__send__ *args, &block
> end
> end
>=20
> If you use this class to wrap an object, code that relies entirely on
> duck typing will still be able to use the object, even though it won't
> respond_to? much of anything, and it won't be kind_of? the class you
> want.


Well, on this point, isn't it recommended that whenever you override
method_missing you should also override respond_to? as well so that
they agree? I thought this was in the docs but can't find it now in
either ri method_missing or ri respond_to. Despite that, I know I've
heard it mentioned various times on this list and in general it seems
like a Good Idea.

Jacob Fugal


 
Reply With Quote
 
Mark Hubbart
Guest
Posts: n/a
 
      05-23-2005
On 5/23/05, Jacob Fugal <(E-Mail Removed)> wrote:
> On 5/23/05, Mark Hubbart <(E-Mail Removed)> wrote:
> > Use of a "uniform" array would seem to preclude duck-typing. Even if
> > you use respond_to? instead of kind_of?, it wouldn't catch objects
> > that use method_missing to handle messages. For example:
> >
> > class Wrapper
> > def initialize(obj)
> > @obj =3D obj
> > end
> > def method_missing(*args, &block)
> > @obj.__send__ *args, &block
> > end
> > end
> >
> > If you use this class to wrap an object, code that relies entirely on
> > duck typing will still be able to use the object, even though it won't
> > respond_to? much of anything, and it won't be kind_of? the class you
> > want.

>=20
> Well, on this point, isn't it recommended that whenever you override
> method_missing you should also override respond_to? as well so that
> they agree? I thought this was in the docs but can't find it now in
> either ri method_missing or ri respond_to. Despite that, I know I've
> heard it mentioned various times on this list and in general it seems
> like a Good Idea.


I'd never heard it mentioned on the list (that I remember), but it
seems to be mentioned in the style guide, on the rubygarden wiki. No
disrespect to those who posted it, but what you see on a wiki doesn't
always reflect the views of the entire community. (not that I'm the
authority on what this community thinks)

Grepping the standard library, I found 24 instances of method_missing
being overridden, and 0 instances of respond_to? being overridden:

mark@eMac% ruby -e'p ARGF.grep(/def (\S+\.)?method_missing/).size' **/*.rb
24
mark@eMac% ruby -e'p ARGF.grep(/def (\S+\.)?respond_to\?/).size' **/*.rb
0

So, even if the programmer follows that rule, the stdlib doesn't seem
to, so you can still get objects creeping in that will respond to a
method without raising an error, but don't respond_to? it.

cheers,
Mark


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Re: How include a large array? Edward A. Falk C Programming 1 04-04-2013 08:07 PM
sure shot requirement for C++ with Unix Experience !!! sure interviewto day !!! Himayat66@gmail.com ASP .Net 0 02-07-2008 12:40 AM
'' is not a valid name. Make sure that it does not include invalid characters or punctuation and that it is not too long. rote ASP .Net 2 01-23-2008 03:07 PM
Free memory allocate by a STL vector, vector of vector, map of vector Allerdyce.John@gmail.com C++ 8 02-18-2006 12:48 AM
popen: how to make sure child process is terminated and get return code? Tung Wai Yip Python 1 10-30-2003 09:40 AM



Advertisments