Iterator Fu Failing Me

On Jan 7, 2006, at 12:19 PM, Bob H. 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 H. – 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 H. – 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/

On Sun, Jan 08, 2006 at 01:13:00AM +0900, Robert K. 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. :slight_smile: And then it’s much
more inelegant than using #detect. :slight_smile:

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

On Jan 7, 2006, at 12:19 PM, Bob H. 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 H. – 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/

On Jan 7, 2006, at 7:33 AM, Robert K. 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 G. II

On 2006.01.07 23:49, [email protected] 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 :slight_smile:

regards.
-a

E

Bob H. [email protected] 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

Hi –

On Sun, 8 Jan 2006, [email protected] 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
[email protected]

“Ruby for Rails”, from Manning Publications, coming April 2006!

On Jan 7, 2006, at 1:08 PM, Robert K. 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 H. – 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/

On Jan 7, 2006, at 5:43 PM, Robert K. 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 :slight_smile:

robert


Bob H. – 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/

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

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.

James Edward G. II

On Mon, 09 Jan 2006 01:45:11 -0000, Ross B.
[email protected] 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.

On Mon, 09 Jan 2006 01:09:23 -0000, James Edward G. II
[email protected] wrote:

It works just fine, yes. Just doesn’t feel very Rubyish to me.

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.

On Mon, 09 Jan 2006 02:17:26 -0000, Ross B.
[email protected] 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.

On Jan 8, 2006, at 9:38 PM, Ross B. 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. :slight_smile:


Ross B. - [email protected]


Bob H. – 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/

On Mon, 09 Jan 2006 01:45:11 -0000, Ross B.
[email protected] wrote:

 val

end

It works just fine, yes. Just doesn’t feel very Rubyish to me.

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.

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

On Sun, 8 Jan 2006 [email protected] 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