For infinite ranges you can't call count, you have to call size.
irb> (1..Float::INFINITY).count # have to interrupt to stop it
irb> (1..Float::INFINITY).size
=> Infinity
The problem with this is that Range is an Enumerable. Enumerable does not have size it has count. So, if you want to implement a method for any Enumerable and you want to check the number of items you can't rely on count. Instead you have to do:
enum_count =
begin
size
rescue NameError
count
end
Making Range#count as an alias of Range#size would allow people to make methods for Enumerable classes that rely on count.
I should have considered that. I was focused on the use case at hand. :(
I still feel like there needs to be a way to do this that's safe. Perhaps the solution is adding Enumerable#size and letting size be the option that's safe for infinite lists.
size calculates the size lazily, without enumerating. If it can't, it returns nil. count calculates the size by doing the actual enumeration. It never returns nil, but may never finish.
(1..10**8).count # => takes a few seconds
(1..10**8).size # => instant
Note that some ranges don't have sizes (mostly because I was too lazy to implement it for ranges of strings):
PS: "Enumerable does not have size" is incorrect. Enumerable have a size method, although it may return nil if the result can not be calculated lazily.
PS: "Enumerable does not have size" is incorrect. Enumerable have a size method, although it may return nil if the result can not be calculated lazily.
I don't think that's true. The docs don't show it and it doesn't get added when you include Enumerable.
[1] pry(main)> class Foo
[1] pry(main)* include Enumerable
[1] pry(main)* end
=> Foo
[2] pry(main)> Foo.methods.include?(:size)
=> false
[3] pry(main)>
Aside from that, it means you can't create functions that are designed to work with Enumerable classes and depend on anything to get a proper size/length/count from them.