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
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?
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
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
#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.
···
--- Ben <benbelly@gmail.com> wrote:
What am I missing? Am I doing this entirely wrong for Ruby?
-Ben
__________________________________
Yahoo! Mail - PC Magazine Editors' Choice 2005
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
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:
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:
1) Call .class.to_s on each parameter to match the type:
"pattern.class.to_s =~ /Array/ then # I have an array of regexp
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.
···
On Mon, Sep 26, 2005 at 10:38:16AM +0900, Ben wrote:
1) Call .class.to_s on each parameter to match the type:
"pattern.class.to_s =~ /Array/ then # I have an array of regexp
You can do this:
if pattern.class == Array # then ...
Better is:
if pattern.kind_of?(Array)
...
end
because someone might want to subclass arrays and still be able to pass them to your code. If you demand only unmodified array classes, you're making the code unnecessarily inflexible.
Even better would be to test for the specific methods you need using object.responds_to?(selector), i.e. to use duck typing. For example, you might need an enumerable object, in which case you'd check if pattern.responds_to?(each). Then I could pass in a SQL query response that happened to be enumerable and return patterns when each element was coerced via #to_s...
mathew
···
On Mon, Sep 26, 2005 at 10:38:16AM +0900, Ben wrote:
"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: