Enumerable#to_a can run forever

It’s possible to implement an each method in a class mixing in
Enumerable that runs forever, and thus causing, for example, to_a to run
forever. One such class is mathn’s Prime class:

require ‘mathn’
Prime.new.to_a # ← runs forever, never returns

This seems a bit broken to me, and I was writing a program where I
needed the first n primes, or all the primes under 200, for example, so
I re-wrote Enumerable#to_a like so to make my code more idiomatic:

----8<----
module Enumerable
def to_a(n = nil)
result = []
inclusion_condition = true
return result if n.to_i <= 0 and !block_given?
each do |*elements|
elements = elements.first if elements.size == 1
if block_given?
values = result.last
inclusion_condition = yield(*values) unless values.nil?
else
inclusion_condition = n != result.size
end

  if inclusion_condition
    result << elements
  else
    result.pop if block_given?
    break result
  end
end
result

end
end
----8<----

This allows, for example, the following:
Prime.new.to_a(10) # => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] , the first
10 primes
Prime.new.to_a {|prime| prime < 20} # => [2, 3, 5, 7, 11, 13, 17, 19] ,
all the primes under 20

Passed an integer paramater, the code will return an array that long, or
until the Enumerable runs out. Passed a block, it will return an array
of elements, adding elements until the block evaluates to false.

This code surely could use some hacking, refactoring, and possibly bug
fixing. I’ve made a post[1] on http://refactormycode.com just for that
purpose.

Thoughts?

[1]
http://refactormycode.com/codes/196-enumerable-to_a-with-terminating-conditions

-Ben Kudria

http://ben.kudria.net | Jabber: [email protected]

Yukihiro M. wrote:

Hmm, in 1.9, you have Enumerable#take to work like your to_i(num), and
Enumerable#take_while like your to_i{…}.

p Prime.new.take(10) # => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
p Prime.new.take_while{|prime| prime < 20} # => [2, 3, 5, 7, 11, 13,
17, 19]

          matz.

Aaah, ok. Those would be excellent. I look forward to 1.9! Thank you!
However, what about Prime.new.to_a ? That would still run forever, and
the unsuspecting coder might be caught by surprise.

Hi,

In message “Re: Enumerable#to_a can run forever”
on Tue, 25 Dec 2007 08:47:48 +0900, Benjamin K. [email protected]
writes:

|require ‘mathn’
|Prime.new.to_a # ← runs forever, never returns
|
|This seems a bit broken to me, and I was writing a program where I
|needed the first n primes, or all the primes under 200, for example, so
|I re-wrote Enumerable#to_a like so to make my code more idiomatic:

|This allows, for example, the following:
|Prime.new.to_a(10) # => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] , the first
|10 primes
|Prime.new.to_a {|prime| prime < 20} # => [2, 3, 5, 7, 11, 13, 17, 19] ,
|all the primes under 20

Hmm, in 1.9, you have Enumerable#take to work like your to_i(num), and
Enumerable#take_while like your to_i{…}.

p Prime.new.take(10) # => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
p Prime.new.take_while{|prime| prime < 20} # => [2, 3, 5, 7, 11, 13,
17, 19]

          matz.

Hi,

In message “Re: Enumerable#to_a can run forever”
on Tue, 25 Dec 2007 11:27:29 +0900, Benjamin K. [email protected]
writes:

|Aaah, ok. Those would be excellent. I look forward to 1.9! Thank you!
|However, what about Prime.new.to_a ? That would still run forever, and
|the unsuspecting coder might be caught by surprise.

It’s no way to avoid entering infinite loop when you do some
operations on infinite enumerable, such as Prime. For example, trying
#sort or #max on Prime, or even putting it as a splat will cause
infinite loop. Adding an argument or a block to #to_a does not
improve the situation a lot, I think.

          matz.

Yukihiro M. wrote:

It’s no way to avoid entering infinite loop when you do some
operations on infinite enumerable, such as Prime. For example, trying
#sort or #max on Prime, or even putting it as a splat will cause
infinite loop. Adding an argument or a block to #to_a does not
improve the situation a lot, I think.

          matz.

Fair enough - but then does it make sense to have these operations
defined?
Enumerable seems to mean the same thing as “countable”, but it includes
methods that apply to only countably finite sets. Perhaps there should
be a way to distinguish between sets that are countable finite and those
that are countably infinite? Does 1.9 include provisions for handling
infinite lists or sets?

Thanks,
Ben Kudria

http://ben.kudria.net | Jabber: [email protected]

Yukihiro M. wrote:

|Fair enough - but then does it make sense to have these operations
|defined?

Because it’s very pragmatically useful for most of the case.
Theoretical cleanliness is not my concern.

Aah, I see. I guess I agree.

|Perhaps there should
|be a way to distinguish between sets that are countable finite and those
|that are countably infinite?

And adding one burden to library implementer (including me)?

Fair enough :). It would probably be overly confusing for the coder,
too.

|Does 1.9 include provisions for handling
|infinite lists or sets?

I think you mean generator. If so, it has been supported since 1.8.

You are correct, I see Generators in 1.8 can do everything I want.
Seems I don’t know Ruby as well as I thought. :slight_smile:

Thanks for clarifying everything.

Ben Kudria

http://ben.kudria.net | Jabber: [email protected]

Hi,

In message “Re: Enumerable#to_a can run forever”
on Tue, 25 Dec 2007 13:00:48 +0900, Benjamin K. [email protected]
writes:

|Fair enough - but then does it make sense to have these operations
|defined?

Because it’s very pragmatically useful for most of the case.
Theoretical cleanliness is not my concern.

|Perhaps there should
|be a way to distinguish between sets that are countable finite and those
|that are countably infinite?

And adding one burden to library implementer (including me)?

|Does 1.9 include provisions for handling
|infinite lists or sets?

I think you mean generator. If so, it has been supported since 1.8.

          matz.