Actions
Feature #6636
closedEnumerable#size
Feature #6636: Enumerable#size
Description
Now that it has been made clear that Enumerable#count never calls #size and that we have Enumerable#lazy, let me propose again an API for a lazy way to get the size of an Enumerable: Enumerable#size.
- call-seq:
- enum.size # => nil, Integer or Float::INFINITY
- Returns the number of elements that will be yielded, without going through
- the iteration (i.e. lazy), or +nil+ if it can't be calculated lazily.
- perm = (1..100).to_a.permutation(4)
- perm.size # => 94109400
- perm.each_cons(2).size # => 94109399
- loop.size # => Float::INFINITY
- [42].drop_while.size # => nil
About 66 core methods returning enumerators would have a lazy size, like each_slice, permutation or lazy.take.
A few would have size return nil:
Array#{r}index, {take|drop}_while
Enumerable#find{_index}, {take|drop}_while
IO: all methods
Sized enumerators can also be created naturally by providing a block to to_enum/enum_for or a lambda to Enumerator.new.
Example for to_enum:
class Integer def composition return to_enum(:composition){ 1 << (self - 1) } unless block_given? yield [] if zero? downto(1) do |i| (self - i).composition do |comp| yield [i, *comp] end end end end 4.composition.to_a # => [[4], [3, 1], [2, 2], [2, 1, 1], [1, 3], [1, 2, 1], [1, 1, 2], [1, 1, 1, 1]] 42.composition.size # => 2199023255552 Example for Enumerator.new:
def lazy_product(*enums) sizer = ->{ enums.inject(1) do |product, e| break if (size = e.size).nil? product * size end } Enumerator.new(sizer) do |yielder| # ... generate combinations end end lazy_product(1..4, (1..3).each_cons(2)).size # => 8 lazy_product(1..4, (1..3).cycle).size # => Float::INFINITY Files
Actions