Iterator Fu Failing Me

  • Thread starter James Edward Gray II
  • Start date
J

James Edward Gray II

I have a group of classes, all implementing a parse?() class method.
Calling parse?(token) will return the constructed object, if it can
be parsed by this class, or false otherwise.

I want to run a bunch of tokens through these classes, grabbing the
first fit. For example:

elements = tokens.map do |token|
ClassA.parse?(token) or
ClassB.parse?(token) or
ClassC.parse?(token)
end

That works. Now can anyone give me a version of the middle section
that doesn't require I call parse?() 50 times? I want something
close to:

elements = tokens.map do |token|
[ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
end

Except that I want the return result of parse?(), instead of the
class that took it.

Thanks for any tips you can offer.

James Edward Gray II
 
M

Marcel Molina Jr.

I have a group of classes, all implementing a parse?() class method.
Calling parse?(token) will return the constructed object, if it can
be parsed by this class, or false otherwise.

I want to run a bunch of tokens through these classes, grabbing the
first fit. For example:

elements = tokens.map do |token|
ClassA.parse?(token) or
ClassB.parse?(token) or
ClassC.parse?(token)
end

That works. Now can anyone give me a version of the middle section
that doesn't require I call parse?() 50 times? I want something
close to:

elements = tokens.map do |token|
[ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
end

Except that I want the return result of parse?(), instead of the
class that took it.

do/end doesn't bind tightly enough for the assignment to 'elements'. Using {}
should work.

marcel
 
J

James Edward Gray II

elements = tokens.map do |token|
[ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
end

Except that I want the return result of parse?(), instead of the
class that took it.

do/end doesn't bind tightly enough for the assignment to
'elements'. Using {}
should work.

Sure it does:
=> [2, 4, 6]

My problem is not syntax. It's that I can't find a way to iterate to
the result of parse?().

James Edward Gray II
 
M

Michael Q. Harris

Are "Write a new Enumerable method that mimics find but returns the
block result instead" or "do a less elegant solution involving
assignment" not acceptable solutions?
 
G

GFunk913

Are "Write a new Enumerable method that mimics find but returns the
block result instead" or "do a less elegant solution involving
assignment" not acceptable solutions?
 
A

ara.t.howard

I have a group of classes, all implementing a parse?() class method. Calling
parse?(token) will return the constructed object, if it can be parsed by this
class, or false otherwise.

I want to run a bunch of tokens through these classes, grabbing the first
fit. For example:

elements = tokens.map do |token|
ClassA.parse?(token) or
ClassB.parse?(token) or
ClassC.parse?(token)
end

That works. Now can anyone give me a version of the middle section that
doesn't require I call parse?() 50 times? I want something close to:

elements = tokens.map do |token|
[ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
end

Except that I want the return result of parse?(), instead of the class that
took it.


harp:~ > cat a.rb

module Parser
module ClassMethods
def parse tokens, klasses
tokens.map{|token| catch('parse!'){ klasses.each{|klass| klass.parse! token}; nil}}
end
def parse! token
ret = parse?(token) and throw 'parse!', ret
end
def parse? token
return "<#{ token }> parsed by <#{ self }>" if token =~ self::RE
end
end
module InstanceMethods
end
def self::included other
other.module_eval{ extend ClassMethods and include InstanceMethods }
end
extend ClassMethods and include InstanceMethods
end

class ClassA
include Parser
RE = /a/
end

class ClassB
include Parser
RE = /b/
end

class ClassC
include Parser
RE = /c/
end

elements = Parser::parse %w( a b c d ), [ClassA, ClassB, ClassC]

p elements


harp:~ > ruby a.rb
["<a> parsed by <ClassA>", "<b> parsed by <ClassB>", "<c> parsed by <ClassC>", nil]


you can probably come up with something shorter - but using catch/throw with
parsers is very useful to break of conditions like these.

cheers.

-a
--
===============================================================================
| ara [dot] t [dot] howard [at] noaa [dot] gov
| all happiness comes from the desire for others to be happy. all misery
| comes from the desire for oneself to be happy.
| -- bodhicaryavatara
===============================================================================
 
E

Eero Saynatkari

I have a group of classes, all implementing a parse?() class method.
Calling parse?(token) will return the constructed object, if it can
be parsed by this class, or false otherwise.

I want to run a bunch of tokens through these classes, grabbing the
first fit. For example:

elements = tokens.map do |token|
ClassA.parse?(token) or
ClassB.parse?(token) or
ClassC.parse?(token)
end

That works. Now can anyone give me a version of the middle section
that doesn't require I call parse?() 50 times? I want something
close to:

elements = tokens.map do |token|
[ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
end

# Not tested
elements = tokens.map {|token|
[A, B, C].each {|kind|
result = kind.parse?(token) and break result
}
}
Except that I want the return result of parse?(), instead of the
class that took it.

Thanks for any tips you can offer.

James Edward Gray II


E
 
H

Henrik Martensson

I have a group of classes, all implementing a parse?() class method.
Calling parse?(token) will return the constructed object, if it can
be parsed by this class, or false otherwise.

I want to run a bunch of tokens through these classes, grabbing the
first fit. For example:

elements = tokens.map do |token|
ClassA.parse?(token) or
ClassB.parse?(token) or
ClassC.parse?(token)
end

That works. Now can anyone give me a version of the middle section
that doesn't require I call parse?() 50 times? I want something

I am not certain if this solution fits, but...

There is a refactoring named Replace Conditional with Polymorphism that
might solve your problem. The idea is that when you have a conditional,
you can create a class hierarchy with one subclass for each leg in the
conditional. (See
http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html for a slightly better explanation.)

You already have several different parser classes, and of course you
don't need to bother with a class hierarchy, so you could do something
like:

parser = ParserFactory.create(parser_type)
...
elements = tokens.map do |token|
parser.parse?(token)
end

Note that 'parser_type' may well be the class of a token in tokens.

This works assuming that all tokens in 'tokens' are of the same type.
For example, you have one parser for handling CSV data, another for XML,
a third for SGML, a fourth for non-compliant HTML, etc.

On the other hand, if each parser class handles a subset of the tokens
you get from one type of input, for example you have tokenized an XML
file, and have one parser for elements, another for processing
instructions, a third for text nodes, etc., you will need something
different. A case statement would do the trick:

elements = tokens.map do |token|
case token
when Element
ElementParser.parse?(token)
when Text
TextParser.parse?(token)
...
else
raise "Unknown token type"
end
end

You can then factor out the case statement into a method named parse?,
move the parse? method to a class of it's own, and be back to:

elements = tokens.map do |token|
ParseEverything.parse?(token)
end

A while ago I wrote a CSV parser in Java for a talk on Test Driven
Design. The Java code got horribly complex, but it struck me that in
Ruby I could do this:

class Parser
def parse(reader, writer)
tokenize(reader) { |type| |value|
write(type, value, writer)
}
end
end

By mixing in tokenize and write methods, it would be possible to build
parsers that handle most formats.

I hope this helps.

/Henrik
 
B

Brian Mitchell

I have a group of classes, all implementing a parse?() class method.
Calling parse?(token) will return the constructed object, if it can
be parsed by this class, or false otherwise.

I want to run a bunch of tokens through these classes, grabbing the
first fit. For example:

elements =3D tokens.map do |token|
ClassA.parse?(token) or
ClassB.parse?(token) or
ClassC.parse?(token)
end

That works. Now can anyone give me a version of the middle section
that doesn't require I call parse?() 50 times? I want something
close to:

elements =3D tokens.map do |token|
[ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
end

How about:

elements =3D tokens.map do |token|
[ClassA, ClassB, ClassC].inject(false) {|m, kind| m or kind.parse?(token)=
}
end

It isn't perfectly efficient but it is short.

Brian.
 
R

Robert Klemme

James Edward Gray II said:
I have a group of classes, all implementing a parse?() class method.
Calling parse?(token) will return the constructed object, if it can
be parsed by this class, or false otherwise.

I want to run a bunch of tokens through these classes, grabbing the
first fit. For example:

elements = tokens.map do |token|
ClassA.parse?(token) or
ClassB.parse?(token) or
ClassC.parse?(token)
end

That works. Now can anyone give me a version of the middle section
that doesn't require I call parse?() 50 times? I want something
close to:

elements = tokens.map do |token|
[ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
end

Except that I want the return result of parse?(), instead of the
class that took it.

Thanks for any tips you can offer.

James Edward Gray II

elements = tokens.map do |tok|
parsers.inject(false) {|a,par| a=par.parse(tok) and break a}
end

alternative using a method definition

def parse(token)
parsers.each {|par| a=par.parse(token) and return a}
false
end

But the best solution is

elements = tokens.map do |tok|
parsers.detect {|par| par.parse(tok)}
end

:)

Kind regards

robert
 
A

ara.t.howard

I have a group of classes, all implementing a parse?() class method.
Calling parse?(token) will return the constructed object, if it can
be parsed by this class, or false otherwise.

I want to run a bunch of tokens through these classes, grabbing the
first fit. For example:

elements = tokens.map do |token|
ClassA.parse?(token) or
ClassB.parse?(token) or
ClassC.parse?(token)
end

That works. Now can anyone give me a version of the middle section
that doesn't require I call parse?() 50 times? I want something
close to:

elements = tokens.map do |token|
[ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
end

# Not tested
elements = tokens.map {|token|
[A, B, C].each {|kind|
result = kind.parse?(token) and break result
}
}

fails when no kind parses. in this case the result will be

[[A,B,C]] (the return of each...)

regards.

-a
--
===============================================================================
| ara [dot] t [dot] howard [at] noaa [dot] gov
| strong and healthy,
| who thinks of sickness until it strikes like lightning?
| preoccupied with the world,
| who thinks of death, until it arrives like thunder?
| -- milarepa
===============================================================================
 
A

ara.t.howard

elements = tokens.map do |tok|
parsers.inject(false) {|a,par| a=par.parse(tok) and break a}
end

nice.

-a
--
===============================================================================
| ara [dot] t [dot] howard [at] noaa [dot] gov
| strong and healthy,
| who thinks of sickness until it strikes like lightning?
| preoccupied with the world,
| who thinks of death, until it arrives like thunder?
| -- milarepa
===============================================================================
 
B

Bob Hutchison

That works. Now can anyone give me a version of the middle section
that doesn't require I call parse?() 50 times? I want something
close to:

elements = tokens.map do |token|
[ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
end

Except that I want the return result of parse?(), instead of the
class that took it.

Why not:

elements = tokens.map do |token|
result = nil
[ClassA, ClassB, ClassC].find { |kind| result = kind.parse?(token) }
end


----
Bob Hutchison -- blogs at <http://www.recursive.ca/
hutch/>
Recursive Design Inc. -- <http://www.recursive.ca/>
Raconteur -- <http://www.raconteur.info/>
xampl for Ruby -- <http://rubyforge.org/projects/xampl/>
 
R

Robert Klemme

Bob Hutchison said:
That works. Now can anyone give me a version of the middle section
that doesn't require I call parse?() 50 times? I want something
close to:

elements = tokens.map do |token|
[ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
end

Except that I want the return result of parse?(), instead of the
class that took it.

Why not:

elements = tokens.map do |token|
result = nil
[ClassA, ClassB, ClassC].find { |kind| result = kind.parse?(token) }
end

Because this won't return the proper value from the block. You need at
least to add a line with "result" after the #find. :) And then it's much
more inelegant than using #detect. :)

Kind regards

robert
 
P

Pierre Barbier de Reuille

(e-mail address removed) a écrit :
I have a group of classes, all implementing a parse?() class method.
Calling parse?(token) will return the constructed object, if it can be
parsed by this class, or false otherwise.

I want to run a bunch of tokens through these classes, grabbing the
first fit. For example:

elements = tokens.map do |token|
ClassA.parse?(token) or
ClassB.parse?(token) or
ClassC.parse?(token)
end

That works. Now can anyone give me a version of the middle section
that doesn't require I call parse?() 50 times? I want something close
to:

elements = tokens.map do |token|
[ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
end

Except that I want the return result of parse?(), instead of the class
that took it.



harp:~ > cat a.rb

module Parser
module ClassMethods
def parse tokens, klasses
tokens.map{|token| catch('parse!'){ klasses.each{|klass|
klass.parse! token}; nil}}
end
def parse! token
ret = parse?(token) and throw 'parse!', ret
end
def parse? token
return "<#{ token }> parsed by <#{ self }>" if token =~ self::RE
end
end
module InstanceMethods
end
def self::included other
other.module_eval{ extend ClassMethods and include InstanceMethods }
end
extend ClassMethods and include InstanceMethods
end

Could you explain this module ?

I think I can understand why you defined a module ClassMethods : to
include class methods and not only instace methods.

However, I don't understant why the InstanceMethods module ? What do you
expect with this module ? Is it just for symetry ?

Thanks !
 
P

Pierre Barbier de Reuille

Robert Klemme a écrit :
Bob Hutchison said:
That works. Now can anyone give me a version of the middle section
that doesn't require I call parse?() 50 times? I want something
close to:

elements = tokens.map do |token|
[ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
end

Except that I want the return result of parse?(), instead of the
class that took it.


Why not:

elements = tokens.map do |token|
result = nil
[ClassA, ClassB, ClassC].find { |kind| result = kind.parse?(token) }
end


Because this won't return the proper value from the block. You need at
least to add a line with "result" after the #find. :) And then it's
much more inelegant than using #detect. :)

Kind regards

robert

Well, looking at the documentation of ruby1.8, detect and find are
aliases ... is it specific to ruby1.8 or some newer/older version ?

Pierre
 
A

ara.t.howard

Could you explain this module ?

I think I can understand why you defined a module ClassMethods : to include
class methods and not only instace methods.

However, I don't understant why the InstanceMethods module ? What do you
expect with this module ? Is it just for symetry ?

yes. just symetry/clarity. is use this pattern alot. even if someone
doesn't understand the mechanism i generally assume the 'ClassMethods' and
'InstanceMethods' names will give it away.

cheers.

-a
--
===============================================================================
| ara [dot] t [dot] howard [at] noaa [dot] gov
| strong and healthy,
| who thinks of sickness until it strikes like lightning?
| preoccupied with the world,
| who thinks of death, until it arrives like thunder?
| -- milarepa
===============================================================================
 
B

Bob Hutchison

Bob Hutchison said:
That works. Now can anyone give me a version of the middle section
that doesn't require I call parse?() 50 times? I want something
close to:

elements = tokens.map do |token|
[ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
end

Except that I want the return result of parse?(), instead of the
class that took it.

Why not:

elements = tokens.map do |token|
result = nil
[ClassA, ClassB, ClassC].find { |kind| result = kind.parse?
(token) }
end

Because this won't return the proper value from the block. You
need at least to add a line with "result" after the #find. :)

cut'n'past-o... oops
And then it's much more inelegant than using #detect. :)

You mean #detect or #select?

elements = tokens.select do |token|
result = nil
[ClassA, ClassB, ClassC].find { |kind| results << kind.parse?
(token) }
result
end

And this is kind of handy too...

elements = tokens.map do |token|
result = nil
[ClassA, ClassB, ClassC].find { |kind| result = [token, kind.parse?
(token)] }
result
end

Kind regards

robert

----
Bob Hutchison -- blogs at <http://www.recursive.ca/
hutch/>
Recursive Design Inc. -- <http://www.recursive.ca/>
Raconteur -- <http://www.raconteur.info/>
xampl for Ruby -- <http://rubyforge.org/projects/xampl/>
 
R

Robert Klemme

Bob Hutchison said:
Bob Hutchison said:
On Jan 6, 2006, at 11:36 PM, James Edward Gray II wrote:

That works. Now can anyone give me a version of the middle section
that doesn't require I call parse?() 50 times? I want something
close to:

elements = tokens.map do |token|
[ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
end

Except that I want the return result of parse?(), instead of the
class that took it.

Why not:

elements = tokens.map do |token|
result = nil
[ClassA, ClassB, ClassC].find { |kind| result = kind.parse?
(token) }
end

Because this won't return the proper value from the block. You
need at least to add a line with "result" after the #find. :)

cut'n'past-o... oops
And then it's much more inelegant than using #detect. :)

You mean #detect or #select?

#detect - because it stops as soon as it has a hit while #select will return
an array. We need just the first hit.
[nil,nil,nil,"w",2,3].find {|x|puts x;x}
nil
nil
nil
w
=> "w"
[nil,nil,nil,"w",2,3].select {|x|puts x;x}
nil
nil
nil
w
2
3
=> ["w", 2, 3]
elements = tokens.select do |token|
result = nil
[ClassA, ClassB, ClassC].find { |kind| results << kind.parse?
(token) }
result
end

You don't define results and you never assign result another value than
nil...
And this is kind of handy too...

elements = tokens.map do |token|
result = nil
[ClassA, ClassB, ClassC].find { |kind| result = [token, kind.parse?
(token)] }
result
end

There's no point in storing token in result because it's unchanged and
available as block parameter.

Say what you want, find/detect is the most elegant solution.

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

Forum statistics

Threads
474,201
Messages
2,571,049
Members
47,655
Latest member
eizareri

Latest Threads

Top