Feature #13495
closedadd Range#count as an alias to Range#size
Description
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.
Updated by Hanmac (Hans Mackowiak) over 8 years ago
we can't do that.
Enumerable#count does can take a block with you can define that you want to count.
(1..4).count(&:even?) #=> 2
so we can't make Range#count be an alias for Range#size without breaking other stuff.
Updated by AaronLasseigne (Aaron Lasseigne) over 8 years ago
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.
Updated by marcandre (Marc-Andre Lafortune) over 8 years ago
- Status changed from Open to Rejected
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):
('a1'..'zz').size # => nil
('a1'..'zz').count # => 259
In short: understand the difference and use whichever method is right for the task.
Updated by marcandre (Marc-Andre Lafortune) over 8 years ago
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.
Updated by AaronLasseigne (Aaron Lasseigne) over 8 years ago
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.
Updated by marcandre (Marc-Andre Lafortune) over 8 years ago
Sorry, I got confused. Enumerator has a size method, not Enumerable.