Iterator Fu Failing Me

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

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

marcel

···

On Sat, Jan 07, 2006 at 01:36:30PM +0900, James Edward Gray II wrote:

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.

--
Marcel Molina Jr. <marcel@vernix.org>

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?

James Edward Gray II wrote:

···

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

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

···

On Sat, 7 Jan 2006, James Edward Gray II wrote:

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.

--

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

===============================================================================

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

···

On 2006.01.07 13:36, James Edward Gray II wrote:

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?

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
Replace Conditional with Polymorphism 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

···

On Sat, 2006-01-07 at 05:36, James Edward Gray II wrote:

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

--

http://www.henrikmartensson.org/ - Reflections on software development

How about:

elements = 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.

···

On 1/6/06, James Edward Gray II <james@grayproductions.net> wrote:

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

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

:slight_smile:

Kind regards

    robert

···

James Edward Gray II <james@grayproductions.net> wrote:

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

Why not:

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

···

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.

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

Perhaps I'm missing the point, but wouldn't the following work: (?)

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

Justin

  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:

>> numbers = (1..3).map do |n|
?> n * 2
>> end
=> [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

···

On Jan 6, 2006, at 10:39 PM, Marcel Molina Jr. wrote:

On Sat, Jan 07, 2006 at 01:36:30PM +0900, James Edward Gray II wrote:

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

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

regards.

-a

···

On Sat, 7 Jan 2006, Eero Saynatkari wrote:

On 2006.01.07 13:36, James Edward Gray II wrote:

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
               }
             }

--

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

===============================================================================

nice.

-a

···

On Sat, 7 Jan 2006, Robert Klemme wrote:

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

--

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

===============================================================================

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

Kind regards

    robert

···

Bob Hutchison <hutch@recursive.ca> wrote:

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

ara.t.howard@noaa.gov a écrit :

···

On Sat, 7 Jan 2006, James Edward Gray II wrote:

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 !

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

That's what I was looking for. Thank you.

But the best solution is

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

That's not equivalent. It's actually the same example I showed in my original question message that didn't work. See that post for details, but the short story is that it finds the parsers that did the conversion, not the parsed elements I was looking for.

James Edward Gray II

···

On Jan 7, 2006, at 7:33 AM, Robert Klemme wrote:

It works just fine, yes. Just doesn't feel very Rubyish to me. <shrugs>

James Edward Gray II

···

On Jan 8, 2006, at 6:58 PM, jwesley wrote:

Perhaps I'm missing the point, but wouldn't the following work: (?)

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

Yeah, but #detect is better here. And if *I* say that another solution is bettern than an inject solution... :slight_smile:

    robert

···

ara.t.howard@noaa.gov wrote:

On Sat, 7 Jan 2006, Robert Klemme wrote:

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

nice.

Robert Klemme a écrit :

···

Bob Hutchison <hutch@recursive.ca> wrote:

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. :slight_smile: And then it's
much more inelegant than using #detect. :slight_smile:

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