Forum: Ruby Iterator Fu Failing Me

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-01-07 05:38
(Received via mailing list)
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
Cee0292fffa691f1fb320d5400200e99?d=identicon&s=25 Marcel Molina Jr. (Guest)
on 2006-01-07 05:41
(Received via mailing list)
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
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-01-07 05:50
(Received via mailing list)
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
B6517199a62a4f8e4e92be9b7e3948e2?d=identicon&s=25 unknown (Guest)
on 2006-01-07 06:41
(Received via mailing list)
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?
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-01-07 07:02
(Received via mailing list)
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
5c7bdd14d6885c8275eaf78be41d120a?d=identicon&s=25 Eero Saynatkari (Guest)
on 2006-01-07 07:17
(Received via mailing list)
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
615466ff7b9a15254d66d7ad27bed5ad?d=identicon&s=25 Michael Q. Harris (Guest)
on 2006-01-07 09:14
(Received via mailing list)
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?
Ded98dc06a045924f0d48b2e46fdf229?d=identicon&s=25 Henrik Martensson (Guest)
on 2006-01-07 11:35
(Received via mailing list)
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/replaceConditio...
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
B44ab09b79ee4a0cc4b4ca69e10eeb3a?d=identicon&s=25 Brian Mitchell (Guest)
on 2006-01-07 12:05
(Received via mailing list)
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.
5befe95e6648daec3dd5728cd36602d0?d=identicon&s=25 Robert Klemme (Guest)
on 2006-01-07 14:35
(Received via mailing list)
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
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-01-07 15:50
(Received via mailing list)
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
===============================================================================
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-01-07 16:29
(Received via mailing list)
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
===============================================================================
5befe95e6648daec3dd5728cd36602d0?d=identicon&s=25 Robert Klemme (Guest)
on 2006-01-07 16:45
(Received via mailing list)
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
E3c79c779c0b390049289cdfe7cb9705?d=identicon&s=25 Bob Hutchison (Guest)
on 2006-01-07 17:06
(Received via mailing list)
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/>
5befe95e6648daec3dd5728cd36602d0?d=identicon&s=25 Robert Klemme (Guest)
on 2006-01-07 17:15
(Received via mailing list)
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
57db258c85d869acb110768cc675cfba?d=identicon&s=25 Pierre Barbier de Reuille (Guest)
on 2006-01-07 17:24
(Received via mailing list)
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 !
57db258c85d869acb110768cc675cfba?d=identicon&s=25 Pierre Barbier de Reuille (Guest)
on 2006-01-07 17:30
(Received via mailing list)
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
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-01-07 17:30
(Received via mailing list)
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
===============================================================================
E3c79c779c0b390049289cdfe7cb9705?d=identicon&s=25 Bob Hutchison (Guest)
on 2006-01-07 18:21
(Received via mailing list)
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/>
5befe95e6648daec3dd5728cd36602d0?d=identicon&s=25 Robert Klemme (Guest)
on 2006-01-07 19:09
(Received via mailing list)
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
E3c79c779c0b390049289cdfe7cb9705?d=identicon&s=25 Bob Hutchison (Guest)
on 2006-01-07 19:21
(Received via mailing list)
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/>
E3c79c779c0b390049289cdfe7cb9705?d=identicon&s=25 Bob Hutchison (Guest)
on 2006-01-07 19:24
(Received via mailing list)
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/>
Dd76a12d66f843de5c5f8782668e7127?d=identicon&s=25 Mauricio Fernandez (Guest)
on 2006-01-07 19:27
(Received via mailing list)
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

...
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-01-07 19:54
(Received via mailing list)
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
E3c79c779c0b390049289cdfe7cb9705?d=identicon&s=25 Bob Hutchison (Guest)
on 2006-01-07 20:15
(Received via mailing list)
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/>
5c7bdd14d6885c8275eaf78be41d120a?d=identicon&s=25 Eero Saynatkari (Guest)
on 2006-01-07 20:43
(Received via mailing list)
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
5befe95e6648daec3dd5728cd36602d0?d=identicon&s=25 Robert Klemme (Guest)
on 2006-01-07 23:44
(Received via mailing list)
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
1fba4539b6cafe2e60a2916fa184fc2f?d=identicon&s=25 unknown (Guest)
on 2006-01-08 00:08
(Received via mailing list)
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
E3c79c779c0b390049289cdfe7cb9705?d=identicon&s=25 Bob Hutchison (Guest)
on 2006-01-08 15:08
(Received via mailing list)
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/>
929f667706d5d2d3513b2c50676ad08c?d=identicon&s=25 jwesley (Guest)
on 2006-01-09 01:58
(Received via mailing list)
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
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2006-01-09 02:10
(Received via mailing list)
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
82e62c756d89bc6fa0a0a2d7f2b1e617?d=identicon&s=25 Ross Bamford (Guest)
on 2006-01-09 02:55
(Received via mailing list)
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.
82e62c756d89bc6fa0a0a2d7f2b1e617?d=identicon&s=25 Ross Bamford (Guest)
on 2006-01-09 03:13
(Received via mailing list)
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.
82e62c756d89bc6fa0a0a2d7f2b1e617?d=identicon&s=25 Ross Bamford (Guest)
on 2006-01-09 03:25
(Received via mailing list)
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.
82e62c756d89bc6fa0a0a2d7f2b1e617?d=identicon&s=25 Ross Bamford (Guest)
on 2006-01-09 03:40
(Received via mailing list)
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.
E3c79c779c0b390049289cdfe7cb9705?d=identicon&s=25 Bob Hutchison (Guest)
on 2006-01-09 14:05
(Received via mailing list)
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/>
Ddbfebb47432f6599da361df6a135c7c?d=identicon&s=25 Adam Shelly (Guest)
on 2006-01-09 19:47
(Received via mailing list)
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
Cb48ca5059faf7409a5ab3745a964696?d=identicon&s=25 unknown (Guest)
on 2006-01-10 01:29
(Received via mailing list)
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
===============================================================================
This topic is locked and can not be replied to.