Iterator Fu Failing Me - Ruby


Forums » Ruby » Iterator Fu Failing Me

Iterator Fu Failing Me
Posted by James Gray (bbazzarrakk)
on 07.01.2006 05:38
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
Re: Iterator Fu Failing Me
Posted by Marcel Molina Jr. (Guest)
on 07.01.2006 05:41
On Sat, Jan 07, 2006 at 01:36:30PM +0900, James Edward Gray II wrote:
>     ClassC.parse?(token)
> 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
Re: Iterator Fu Failing Me
Posted by James Gray (bbazzarrakk)
on 07.01.2006 05:50
On Jan 6, 2006, at 10:39 PM, Marcel Molina Jr. wrote:

> 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
Re: Iterator Fu Failing Me
Posted by unknown (Guest)
on 07.01.2006 06:41
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?
Re: Iterator Fu Failing Me
Posted by unknown (Guest)
on 07.01.2006 07:02
On Sat, 7 Jan 2006, James Edward Gray II wrote:

>    ClassC.parse?(token)
> 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
Re: Iterator Fu Failing Me
Posted by Eero Saynatkari (Guest)
on 07.01.2006 07:17
On 2006.01.07 13:36, James Edward Gray II wrote:
>     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
Re: Iterator Fu Failing Me
Posted by Michael Q. Harris (Guest)
on 07.01.2006 09:14
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?
Re: Iterator Fu Failing Me
Posted by Henrik Martensson (Guest)
on 07.01.2006 11:35
On Sat, 2006-01-07 at 05:36, James Edward Gray II wrote:
>      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

--

http://www.henrikmartensson.org/  - Reflections on software development
Re: Iterator Fu Failing Me
Posted by Brian Mitchell (Guest)
on 07.01.2006 12:05
On 1/6/06, James Edward Gray II <james@grayproductions.net> wrote:
>      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
>

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.
Re: Iterator Fu Failing Me
Posted by Robert Klemme (Guest)
on 07.01.2006 14:35
James Edward Gray II <james@grayproductions.net> wrote:
>     ClassC.parse?(token)
> 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
Re: Iterator Fu Failing Me
Posted by unknown (Guest)
on 07.01.2006 15:50
On Sat, 7 Jan 2006, Eero Saynatkari wrote:

>>     ClassB.parse?(token) or
>
>  # 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
===============================================================================
Re: Iterator Fu Failing Me
Posted by unknown (Guest)
on 07.01.2006 16:29
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.

-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
===============================================================================
Re: Iterator Fu Failing Me
Posted by Robert Klemme (Guest)
on 07.01.2006 16:45
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.

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

    robert
Re: Iterator Fu Failing Me
Posted by Bob Hutchison (Guest)
on 07.01.2006 17:06
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


----
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/>
Re: Iterator Fu Failing Me
Posted by Robert Klemme (Guest)
on 07.01.2006 17:15
Bob Hutchison <hutch@recursive.ca> wrote:
>> 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
Re: Iterator Fu Failing Me
Posted by Pierre Barbier de Reuille (Guest)
on 07.01.2006 17:24
ara.t.howard@noaa.gov a écrit :
>>    ClassA.parse?(token) or
>>  end
>       def parse tokens, klasses
>     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 !
Re: Iterator Fu Failing Me
Posted by Pierre Barbier de Reuille (Guest)
on 07.01.2006 17:30
Robert Klemme a écrit :
>>>   end
>> 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
Re: Iterator Fu Failing Me
Posted by unknown (Guest)
on 07.01.2006 17:30
On Sun, 8 Jan 2006, Pierre Barbier de Reuille wrote:

> 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
===============================================================================
Re: Iterator Fu Failing Me
Posted by Bob Hutchison (Guest)
on 07.01.2006 18:21
On Jan 7, 2006, at 11:13 AM, Robert Klemme wrote:

>>>
>
> 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/>
Re: Iterator Fu Failing Me
Posted by Robert Klemme (Guest)
on 07.01.2006 19:09
Bob Hutchison <hutch@recursive.ca> wrote:
>>>>     [ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
>>> (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
Re: Iterator Fu Failing Me
Posted by Bob Hutchison (Guest)
on 07.01.2006 19:21
On Jan 7, 2006, at 12:19 PM, Bob Hutchison wrote:

>>>>   elements = tokens.map do |token|
>>>   [ClassA, ClassB, ClassC].find { |kind| result = kind.parse? 
> You mean #detect or #select?
> elements = tokens.map do |token|
>   result = nil
>   [ClassA, ClassB, ClassC].find { |kind| result = [token,  
> kind.parse?(token)] }
>   result
> end

That's wrong! That's twice I've done that. I'm going to go have a nap
now.

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

Anyway, I like this version better...

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

or this...

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

and you can use "result = (r = kind.parse?(token)) && [r, kind,
token]" if you choose


Cheers,
Bob

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

----
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/>
Re: Iterator Fu Failing Me
Posted by Bob Hutchison (Guest)
on 07.01.2006 19:24
On Jan 7, 2006, at 12:19 PM, Bob Hutchison wrote:

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

This is wrong too! It should be:

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

Now, before I dig anymore holes, I'll stop now.

----
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/>
Re: Iterator Fu Failing Me
Posted by Mauricio Fernandez (Guest)
on 07.01.2006 19:27
On Sun, Jan 08, 2006 at 01:13:00AM +0900, Robert Klemme wrote:
> >>
> 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. :-)

Actually,

enum.c:
    rb_define_method(rb_mEnumerable,"find", enum_find, -1);
    rb_define_method(rb_mEnumerable,"detect", enum_find, -1);

...

(1..10).detect{|x| 10*x > 50}                      # => 6

...
Re: Iterator Fu Failing Me
Posted by James Gray (bbazzarrakk)
on 07.01.2006 19:54
On Jan 7, 2006, at 7:33 AM, Robert Klemme wrote:

> 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
Re: Iterator Fu Failing Me
Posted by Bob Hutchison (Guest)
on 07.01.2006 20:15
On Jan 7, 2006, at 1:08 PM, Robert Klemme wrote:
> w
> => "w"
>>> [nil,nil,nil,"w",2,3].select {|x|puts x;x}
> nil
> nil
> nil
> w
> 2
> 3
> => ["w", 2, 3]
>

The trouble is that detect returns the 'x' not the result of
processing x successfully (or, in terms of the original post, the
class that accepts the token not the result of the #parse? method).
That's why you have to capture the result in the block. Map returns
the result of processing.

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

Sorry, I've 'corrected' myself in a mail I just sent out. (My email
is suffering long delays today, and my mind isn't working all that
quickly either I think)

This is what I had intended with the "<< results" is:

results = []
tokens.each do |token|
   # use find over the collection of classes to find the one that
successfully parses the
   # token. Find will return the class, not the result of #parse?, so
capture that result
   # by using a closure on result (Note: result must appear outside
of and before the block.
   # Collect each result in results.
   result = nil
   [ClassA, ClassB, ClassC].find { |kind| result = kind.parse?(token)}
   results << result if result
end

If you also want the nil results then you can use:

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


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

Only they return the wrong things.

>    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/>
Re: Iterator Fu Failing Me
Posted by Eero Saynatkari (Guest)
on 07.01.2006 20:43
On 2006.01.07 23:49, ara.t.howard@noaa.gov wrote:
> >>  elements = tokens.map do |token|
> >>    [ClassA, ClassB, ClassC].find { |kind| kind.parse?(token) }
> 
>   [[A,B,C]] (the return of each...)

You are (of course) absolutely right, it should be 'return result'
coupled with a default nil/false at the end.

One of these days I will learn not to write mail at four in the
morning :)

> regards.
> -a


E
Re: Iterator Fu Failing Me
Posted by Robert Klemme (Guest)
on 07.01.2006 23:44
Bob Hutchison <hutch@recursive.ca> wrote:
>> nil
>>
>
> The trouble is that detect returns the 'x' not the result of
> processing x successfully (or, in terms of the original post, the
> class that accepts the token not the result of the #parse? method).
> That's why you have to capture the result in the block. Map returns
> the result of processing.

Aargh!  I shoul've tested more carefully.  Yes you're completely right. 
But
the inject version still works, I think that's then my favourite.

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

Thanks for correcting me!

    robert
Re: Iterator Fu Failing Me
Posted by unknown (Guest)
on 08.01.2006 00:08
Hi --

On Sun, 8 Jan 2006, ara.t.howard@noaa.gov wrote:

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

Wouldn't it be clearer just to write instance methods for a module?
It feels a bit like you're creating a kind of second skin for a system
that already does this.


David

--
David A. Black
dblack@wobblini.net

"Ruby for Rails", from Manning Publications, coming April 2006!
http://www.manning.com/books/black
Re: Iterator Fu Failing Me
Posted by Bob Hutchison (Guest)
on 08.01.2006 15:08
On Jan 7, 2006, at 5:43 PM, Robert Klemme wrote:

>>> nil
>>> => ["w", 2, 3]
> favourite.
>
> elements = tokens.map do |tok|
>  parsers.inject(false) {|a,pars| a = pars.parse? tok and break a}
> end

Personally, I don't like the break. It'd be very nice if there were
equivalent operations returning the result of the calculation rather
than the object.

>
> Thanks for correcting me!

Sorry for the brutally finger-tied attempts at communication :-)

>
>    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/>
Re: Iterator Fu Failing Me
Posted by jwesley (Guest)
on 09.01.2006 01:58
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
Re: Iterator Fu Failing Me
Posted by James Gray (bbazzarrakk)
on 09.01.2006 02:10
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

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

James Edward Gray II
Re: Iterator Fu Failing Me
Posted by Ross Bamford (Guest)
on 09.01.2006 02:55
On Mon, 09 Jan 2006 01:09:23 -0000, James Edward Gray II
<james@grayproductions.net> wrote:

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

Not sure this is more Rubyish (seems a bit line-noisy) but:

	class Parser
	  class << self
	    def parse?(tok)
	      tok.downcase if tok == self.name
	    end
	  end
	end

	class One < Parser; end
	class Two < Parser; end
	class Three < Parser; end

	parsers = [One, Two, Three]
	tokens = ['One','Two','One','siz','Four','Two']

	r = tokens.inject [] do |r,token|
	  r << (parsers.zip([token] * parsers.length).detect { |p,tok| p.parse?
tok } || [])[0]
	end

	p r

Seems to work. It's *terribly* inefficient though I guess.
Re: Iterator Fu Failing Me
Posted by Ross Bamford (Guest)
on 09.01.2006 03:13
On Mon, 09 Jan 2006 01:45:11 -0000, Ross Bamford
<rosco@roscopeco.remove.co.uk> wrote:

> Not sure this is more Rubyish (seems a bit line-noisy) but:
>

For crying out loud I've done it again }:-{. Please ignore me.
Re: Iterator Fu Failing Me
Posted by Ross Bamford (Guest)
on 09.01.2006 03:25
On Mon, 09 Jan 2006 01:45:11 -0000, Ross Bamford
<rosco@roscopeco.remove.co.uk> wrote:

>>>      val
>>>    end
>>
>> It works just fine, yes.  Just doesn't feel very Rubyish to me.   
>> <shrugs>
>>
>
> Not sure this is more Rubyish (seems a bit line-noisy) but:
>

This one looks worse, but at least really does seem to work this time:

	r = tokens.inject([]) do |arr,token|
	  arr << parsers.zip([token]*parsers.length).inject(nil) do |out,pt|
	    out or pt[0].parse?(pt[1])
	  end
	end

slotted into the code before and it gives:

	["one", "two", "one", nil, nil, "three"]

Sorry about that.
Re: Iterator Fu Failing Me
Posted by Ross Bamford (Guest)
on 09.01.2006 03:40
On Mon, 09 Jan 2006 02:17:26 -0000, Ross Bamford
<rosco@roscopeco.remove.co.uk> wrote:

> 	r = tokens.inject([]) do |arr,token|
> 	  arr << parsers.zip([token]*parsers.length).inject(nil) do |out,pt|
> 	    out or pt[0].parse?(pt[1])
> 	  end
> 	end
>

Damn it...

  ==

	r = tokens.inject([]) do |arr,token|
	  arr << parsers.inject(nil) { |out,p| out or p.parse?(token) }
	end

Now I'm really done. Promise.
Re: Iterator Fu Failing Me
Posted by Bob Hutchison (Guest)
on 09.01.2006 14:05
On Jan 8, 2006, at 9:38 PM, Ross Bamford wrote:

> Damn it...
>
>  ==
>
> 	r = tokens.inject([]) do |arr,token|
> 	  arr << parsers.inject(nil) { |out,p| out or p.parse?(token) }
> 	end
>
> Now I'm really done. Promise.

This thread is jinxed. :-)

>
> -- 
> Ross Bamford - rosco@roscopeco.remove.co.uk
>

----
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/>
Re: Iterator Fu Failing Me
Posted by Adam Shelly (Guest)
on 09.01.2006 19:47
why not write a new iterator: it will probably be useful again sometime:

module Enumerable
  def find_first_result
    each {|item| result = yield item; return result if result }
    nil
  end
end

irb(main):075:0> tokens = %w( adam codes in ruby )
=> ["adam", "codes", "in", "ruby"]
irb(main):076:0> classes = [/a/, /b/, /c/]
=> [/a/, /b/, /c/]
irb(main):077:0> elements = tokens.map do |token|
irb(main):078:1*   classes.find_first_result {|kind| kind =~ token}
irb(main):079:1> end
=> [0, 0, nil, 2]


-Adam
Re: Iterator Fu Failing Me
Posted by unknown (Guest)
on 10.01.2006 01:29
On Sun, 8 Jan 2006 dblack@wobblini.net wrote:

> Wouldn't it be clearer just to write instance methods for a module?  It
> feels a bit like you're creating a kind of second skin for a system that
> already does this.

generally i do just this.  however, i've found it extremely useful to be 
able
to do


   include SomeModule::InstanceMethods  # mixin only instance methods

   extend SomeModule::ClassMethods # mixin only class methods

   include SomeModule # mixin InstanceMethods and extend with 
ClassMethods via
                      # over-ridden self::included


when factoring out code in large systems.

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