Overloading

B

Ben

I'm a C++ programmer learning Ruby. I would like to make a class that
extracts patterns from a file, and returns an array of values. I
have:

class Extracter
def initialize(pattern, location)
@pattern =3D pattern
@loc =3D location
end
def extract(stream)
pats =3D Array.new
stream.each_line do |line|
if line =3D~ @pattern then
pats << $~[@loc]
end
end
pats
end
end

cppFile =3D File.new("foo.cpp")
incEx =3D Extracter.new(/#include\s*[<"](.*)[>"]/, 1)
includesArr =3D incEx.extract(ccpFile)

So far, so good. If not, let me know what I've done wrong. Next, I
want to extend the Extracter class to hold an array of patterns and
locations, or a Proc parser that will return the interesting value
(this is for lines that are tougher than regexp). How do I create a
constructor that will take different types and create the appropriate
arrays internally? The options I see are:

1) Call .class.to_s on each parameter to match the type:
"pattern.class.to_s =3D~ /Array/ then # I have an array of regexp
2) Use a non-trvial data structure to map symbols to types
3) Use different functions to fill out my extracter:
def add_array
def add_parser

What am I missing? Am I doing this entirely wrong for Ruby?

-Ben
 
E

Edward Faulkner

--i9LlY+UWpKt15+FH
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

The options I see are:
=20
1) Call .class.to_s on each parameter to match the type:
"pattern.class.to_s =3D~ /Array/ then # I have an array of regexp

You can do this:

if pattern.class =3D=3D Array # then ...

Or this:

case pattern
when Array
# handle array
when Regexp
# handle Regexp
end

Or use this to ensure that pattern is always an Array:

pattern =3D [pattern] unless pattern.class =3D=3D Array

-Ed

--i9LlY+UWpKt15+FH
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: Digital signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)

iD8DBQFDN1KJnhUz11p9MSARAgGuAJsFLCtlDvw5THZ0Qnt0CLczvkL+0QCg4XGy
OTpjNhrp1c3D0IM7KY/TUcQ=
=8JVY
-----END PGP SIGNATURE-----

--i9LlY+UWpKt15+FH--
 
B

Ben

You can do this:

if pattern.class =3D=3D Array # then ...

I wasn't aware that I could compare .class with a type name. I guess
I should have realized that. Thanks!

-Ben
 
E

Edward Faulkner

--69pVuxX8awAiJ7fD
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

I wasn't aware that I could compare .class with a type name. I guess
I should have realized that. Thanks!

"Array" isn't a type name, strictly speaking. It's a constant that
refers to the class "Array". And classes are objects, so you can
manipulate them directly. For example:

Array.class
=> Class

Array.ancestors
=> [Array, Enumerable, Object, Kernel]

regards,
Ed

--69pVuxX8awAiJ7fD
Content-Type: application/pgp-signature; name="signature.asc"
Content-Description: Digital signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)

iD8DBQFDN1X5nhUz11p9MSARAjIWAJ0QRbfmhMPftFeI26h594V66JhISwCcDFBd
xI+G/YgHKC4+7Xk81NVJKXQ=
=AMU7
-----END PGP SIGNATURE-----

--69pVuxX8awAiJ7fD--
 
E

Eric Mahurin

--- Ben said:
I'm a C++ programmer learning Ruby. I would like to make a
class that
extracts patterns from a file, and returns an array of
values. I
have:
=20
class Extracter
def initialize(pattern, location)
@pattern =3D pattern
@loc =3D location
end
def extract(stream)
pats =3D Array.new
stream.each_line do |line|
if line =3D~ @pattern then
pats << $~[@loc]
end
end
pats
end
end
=20
cppFile =3D File.new("foo.cpp")
incEx =3D Extracter.new(/#include\s*[<"](.*)[>"]/, 1)
includesArr =3D incEx.extract(ccpFile)
=20
So far, so good. If not, let me know what I've done wrong.=20
Next, I
want to extend the Extracter class to hold an array of
patterns and
locations, or a Proc parser that will return the interesting
value
(this is for lines that are tougher than regexp). How do I
create a
constructor that will take different types and create the
appropriate
arrays internally? The options I see are:
=20
1) Call .class.to_s on each parameter to match the type:
"pattern.class.to_s =3D~ /Array/ then # I have an array
of regexp
2) Use a non-trvial data structure to map symbols to types
3) Use different functions to fill out my extracter:
def add_array
def add_parser

#3 is the best solution for ruby and is the ruby way. What's
the downside? The deeper you get into ruby, the more you'll
find out that overloading methods based on type dillutes
flexibility, offers no advantage, and gives ugly/less readable
code. Overloading methods based on other things (number of
args, flags, etc) is definitely better, but still has some of
the same disadvantages. I try to avoid overloading based on
type at almost all costs and sometime overload based on other
criteria.

What am I missing? Am I doing this entirely wrong for Ruby?
=20
-Ben
=20
=20



=09
__________________________________=20
Yahoo! Mail - PC Magazine Editors' Choice 2005=20
http://mail.yahoo.com
 
G

gabriele renzi

Edward Faulkner ha scritto:
You can do this:

if pattern.class == Array # then ...

maybe just
if pattern.is_a? Array
Or this:

case pattern
when Array
# handle array
when Regexp
# handle Regexp
end

or, use the case-equality method directly:
if pattern === Array


Anyway, I think it would be better to have two different methods.
Given that ruby does not have multiple dispatch, if you ever want to add
another option (i.e. build from an Enumerable) if you hardwire type
checks you'll have to change the method code.
This is especially bad in the face of subclassing, since you actually
have to copy/paste the code.

If you, instead, have different methods for the options, you class would
stay open for changes.
 
R

Robert Klemme

Ben said:
I'm a C++ programmer learning Ruby. I would like to make a class that
extracts patterns from a file, and returns an array of values. I
have:

class Extracter
def initialize(pattern, location)
@pattern = pattern
@loc = location
end
def extract(stream)
pats = Array.new
stream.each_line do |line|
if line =~ @pattern then
pats << $~[@loc]
end
end
pats
end
end

cppFile = File.new("foo.cpp")
incEx = Extracter.new(/#include\s*[<"](.*)[>"]/, 1)
includesArr = incEx.extract(ccpFile)

So far, so good. If not, let me know what I've done wrong. Next, I
want to extend the Extracter class to hold an array of patterns and
locations, or a Proc parser that will return the interesting value
(this is for lines that are tougher than regexp). How do I create a
constructor that will take different types and create the appropriate
arrays internally? The options I see are:

1) Call .class.to_s on each parameter to match the type:
"pattern.class.to_s =~ /Array/ then # I have an array of
regexp 2) Use a non-trvial data structure to map symbols to types
3) Use different functions to fill out my extracter:
def add_array
def add_parser

What am I missing? Am I doing this entirely wrong for Ruby?

General stuff about method overloading and C++ like constructs:
http://www.rubygarden.org/ruby?MethodOverloading
http://www.rubygarden.org/ruby?RubyFromCpp

In your case I'd probably do it differently. Maybe something like this:

def extract(io)
result = []
io.each_line do |line|
dat = yield line and result << dat
end
result
end

includes = File.open("foo.cpp") do |io|
extract io {|line| /#include\s*[<"](.*)[>"]/ =~ line and $1 }
end

Btw, your regexp might be too greedy because you use ".*". Alternatives
that might work:

/#include\s*[<"](.*?)[>"]/
/#include\s*[<"]([^>"]*)[>"]/

Kind regards

robert
 

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,770
Messages
2,569,584
Members
45,077
Latest member
SangMoor21

Latest Threads

Top