Can I stop each's iteration, and get the results so far?

Basically, I want to break from a find_all, and get the array of
elements found so far. Is this possible?

I tried this, no luck with find_all and collect, they return nil,
instead of the array so far. Any suggestions on how to do this?

$ ruby every.rb
3
6
9
12
15
18
21
24
is 20 in every?
nil
is 21 in every?
21
all n between 100 and 200?
nil
all n as a string, up to 20
nil

–> I would like not to have gotten nil for the last two…

$ cat every.rb
class EveryN
include Enumerable

def initialize(n)
@n = n
end

def each
y = 0
loop do
y = y + @n

  yield y
end

end
end

every = EveryN.new(3)

every.each { |n| break if n > 25; puts n }

puts “is 20 in every?”

p every.detect { |n| break if n > 20; n == 20 }

puts “is 21 in every?”

p every.detect { |n| break if n > 21; n == 21 }

puts “all n between 100 and 200?”

p every.find_all { |n| break if n > 200; n >= 100 && n <= 200 }

puts “all n as a string, up to 20”

p every.collect { |n| break if n > 20; “n is #{n}” }

···


Sam Roberts sroberts@certicom.com

Basically, I want to break from a find_all, and get the array of
elements found so far. Is this possible?

I tried this, no luck with find_all and collect, they return nil,
instead of the array so far. Any suggestions on how to do this?

This might be overly simplistic, but if this is not going to be used
by multiple threads simultaneously, and you already know the limit
ahead of time, why not just set the limit on the object before
calling the function, and make sure that each limits you to that?

class EveryN
include Enumerable

def initialize(n)
@n = n
end

def each
y = 0
loop do
y = y + @n
break if @max_iteration < y
yield y
end
end

def max_iteration=(max_iteration)
@max_iteration = max_iteration
end

end

every = EveryN.new(3)
every.max_iteration = 25
every.each { |n| puts n }

puts “is 20 in every?”

every.max_iteration = 20
p every.detect { |n| n == 20 }

puts “is 21 in every?”

every.max_iteration = 21
p every.detect { |n| n == 21 }

puts “all n between 100 and 200?”

every.max_iteration = 200
p every.find_all { |n| n >= 100}

puts “all n as a string, up to 20”

every.max_iteration = 20
p every.collect { |n| “n is #{n}” }

Walt

···

Walter Szewelanczyk
IS Director
M.W. Sewall & CO. email : walter@mwsewall.com
259 Front St. Phone : (207) 442-7994 x 128
Bath, ME 04530 Fax : (207) 443-6284


I can’t think of a way to use ‘break’ here. But you could use
throw and catch.

my_enumerable = (1…1000)

my_enumerable.instance_eval do
@old_each = self.method(:each)
def each
catch :stop_each do
@old_each.call {|n| yield n}
end
end
end

my_enumerable.collect do |n|
throw :stop_each if n > 20
“n is #{n}”
end

#bill

···

On Fri, Mar 19, 2004 at 03:21:16AM +0900, Sam Roberts wrote:

Basically, I want to break from a find_all, and get the array of
elements found so far. Is this possible?

“Sam Roberts” sroberts@certicom.com schrieb im Newsbeitrag
news:20040318181023.GA6610@certicom.com

Basically, I want to break from a find_all, and get the array of
elements found so far. Is this possible?

I tried this, no luck with find_all and collect, they return nil,
instead of the array so far. Any suggestions on how to do this?

#inject is your friend:

puts “all n between 100 and 200?”

p every.find_all { |n| break if n > 200; n >= 100 && n <= 200 }

irb(main):076:0> every.inject() { |arr,n| break arr if n > 200; arr<= 100 && n <= 200; arr }
=> [102, 105, 108, 111, 114, 117, 120, 123, 126, 129, 132, 135, 138, 141,
144, 147, 150, 153, 156, 159, 162, 165, 168, 171, 174, 177, 180, 183, 186,
189, 192, 195, 198]

Regards

robert

Wrote Robert Klemme bob.news@gmx.net, on Fri, Mar 19, 2004 at 05:59:36PM +0900:

Basically, I want to break from a find_all, and get the array of
elements found so far. Is this possible?

I tried this, no luck with find_all and collect, they return nil,
instead of the array so far. Any suggestions on how to do this?

#inject is your friend:

puts “all n between 100 and 200?”

p every.find_all { |n| break if n > 200; n >= 100 && n <= 200 }

irb(main):076:0> every.inject() { |arr,n| break arr if n > 200; arr<= 100 && n <= 200; arr }
=> [102, 105, 108, 111, 114, 117, 120, 123, 126, 129, 132, 135, 138, 141,
144, 147, 150, 153, 156, 159, 162, 165, 168, 171, 174, 177, 180, 183, 186,
189, 192, 195, 198]

Thanks, thats useful to know, and odd!

Why can you break from inject and get the result, but not from find_all
or collect?

Is this a bug, an oversight, or is something about the methods that
makes them need to be different?

Cheers,
Sam

···


Sam Roberts sroberts@certicom.com

Hi –

Wrote Robert Klemme bob.news@gmx.net, on Fri, Mar 19, 2004 at 05:59:36PM +0900:

Basically, I want to break from a find_all, and get the array of
elements found so far. Is this possible?

I tried this, no luck with find_all and collect, they return nil,
instead of the array so far. Any suggestions on how to do this?

#inject is your friend:

puts “all n between 100 and 200?”

p every.find_all { |n| break if n > 200; n >= 100 && n <= 200 }

irb(main):076:0> every.inject() { |arr,n| break arr if n > 200; arr<= 100 && n <= 200; arr }
=> [102, 105, 108, 111, 114, 117, 120, 123, 126, 129, 132, 135, 138, 141,
144, 147, 150, 153, 156, 159, 162, 165, 168, 171, 174, 177, 180, 183, 186,
189, 192, 195, 198]

Thanks, thats useful to know, and odd!

Why can you break from inject and get the result, but not from find_all
or collect?

Is this a bug, an oversight, or is something about the methods that
makes them need to be different?

I think the idea of breaking from find_all is sort of contradictory,
since it runs counter to the “all” part of “find_all” :slight_smile:

But anyway… what Robert does is “break arr” – that is, he gives
break an argument. That argument then becomes the return value of the
whole thing. You can’t do that with find_all, because you don’t have
direct access to the accumulator (“arr” in Robert’s example).

David

···

On Fri, 19 Mar 2004, Sam Roberts wrote:


David A. Black
dblack@wobblini.net

“Sam Roberts” sroberts@certicom.com schrieb im Newsbeitrag
news:20040319143738.GD7332@certicom.com

Wrote Robert Klemme bob.news@gmx.net, on Fri, Mar 19, 2004 at
05:59:36PM +0900:

Basically, I want to break from a find_all, and get the array of
elements found so far. Is this possible?

I tried this, no luck with find_all and collect, they return nil,
instead of the array so far. Any suggestions on how to do this?

#inject is your friend:

puts “all n between 100 and 200?”

p every.find_all { |n| break if n > 200; n >= 100 && n <= 200 }

irb(main):076:0> every.inject() { |arr,n| break arr if n > 200;
arr<= 100 && n <= 200; arr }
=> [102, 105, 108, 111, 114, 117, 120, 123, 126, 129, 132, 135, 138,
141,
144, 147, 150, 153, 156, 159, 162, 165, 168, 171, 174, 177, 180, 183,
186,
189, 192, 195, 198]

Thanks, thats useful to know, and odd!

Why can you break from inject and get the result, but not from find_all
or collect?

Is this a bug, an oversight, or is something about the methods that
makes them need to be different?

The difference is that in the inject version you have access to the array
that collects values and hence can transmit it to the caller via “break
arr”. The find_all version has no access to the array and thus you can’t
return the array via “break”.

I’m not sure whether “find_all” cannot be implemented differently because
I don’t know whether find_all can determine whether there was an argument
to “break” or not. Because if there was then that is clearly what you
want to be returned:

irb(main):006:0> (1…10).find_all {|x| break “early exit” if x % 2 == 0}
=> “early exit”
irb(main):007:0> (1…10).find_all {|x| break “early exit” if x % 26666 ==
0}
=>

Of course you can achieve the same without “inject” by using “return” in a
method:

def test(enum)
arr=
enum.each { |n| return arr if n > 200; arr<= 100 && n <= 200 }
arr
end

Although I have to admit that I find “inject” more beatiful… :slight_smile:

Kind regards

robert

Wrote “David A. Black” dblack@wobblini.net, on Fri, Mar 19, 2004 at 11:58:42PM +0900:

Is this a bug, an oversight, or is something about the methods that
makes them need to be different?

I think the idea of breaking from find_all is sort of contradictory,
since it runs counter to the “all” part of “find_all” :slight_smile:

Hmm, well its not contrary to the idea of breaking from “select” since
it lacks the word “all” :slight_smile:

But anyway… what Robert does is “break arr” – that is, he gives
break an argument. That argument then becomes the return value of the

Ah, I didn’t see that. And didn’t know break could take an argument!

whole thing. You can’t do that with find_all, because you don’t have
direct access to the accumulator (“arr” in Robert’s example).

I see.

Thanks,
Sam

···


Sam Roberts sroberts@certicom.com