From: "mame (Yusuke Endoh)" Date: 2012-10-27T11:51:15+09:00 Subject: [ruby-core:48436] [ruby-trunk - Feature #6636][Assigned] Enumerable#size Issue #6636 has been updated by mame (Yusuke Endoh). Status changed from Open to Assigned Assignee changed from mame (Yusuke Endoh) to matz (Yukihiro Matsumoto) Target version changed from 2.0.0 to next minor ---------------------------------------- Feature #6636: Enumerable#size https://bugs.ruby-lang.org/issues/6636#change-31763 Author: marcandre (Marc-Andre Lafortune) Status: Assigned Priority: Normal Assignee: matz (Yukihiro Matsumoto) Category: core Target version: next minor 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 -- http://bugs.ruby-lang.org/