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 7 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 7 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 7 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 7 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 7 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 7 years ago
Sorry, I got confused. Enumerator
has a size
method, not Enumerable
.