From: "naruse (Yui NARUSE)" Date: 2012-07-21T15:43:52+09:00 Subject: [ruby-core:46585] [ruby-trunk - Feature #6636] Enumerable#size Issue #6636 has been updated by naruse (Yui NARUSE). How about adding Enumerator#receiver and define yourself with it. diff --git a/enumerator.c b/enumerator.c index f01ddd5..8e3ae9a 100644 --- a/enumerator.c +++ b/enumerator.c @@ -942,6 +942,32 @@ enumerator_inspect(VALUE obj) } /* + * call-seq: + * e.receiver -> object + * + * Returns the receiver of this enumerator. + */ + +static VALUE +enumerator_receiver(VALUE obj) +{ + struct enumerator *e; + VALUE eobj; + + TypedData_Get_Struct(obj, struct enumerator, &enumerator_data_type, e); + if (!e || e->obj == Qundef) { + return Qnil; + } + + eobj = rb_attr_get(obj, id_receiver); + if (NIL_P(eobj)) { + eobj = e->obj; + } + + return eobj; +} + +/* * Yielder */ static void @@ -1748,6 +1774,7 @@ InitVM_Enumerator(void) rb_define_method(rb_cEnumerator, "feed", enumerator_feed, 1); rb_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0); rb_define_method(rb_cEnumerator, "inspect", enumerator_inspect, 0); + rb_define_method(rb_cEnumerator, "receiver", enumerator_receiver, 0); /* Lazy */ rb_cLazy = rb_define_class_under(rb_cEnumerator, "Lazy", rb_cEnumerator); irb(main):007:0> e="abcde".enum_for(:each_byte) => # irb(main):009:0> def e.size; receiver.bytesize; end => nil irb(main):010:0> e.size => 5 ---------------------------------------- Feature #6636: Enumerable#size https://bugs.ruby-lang.org/issues/6636#change-28247 Author: marcandre (Marc-Andre Lafortune) Status: Assigned Priority: Normal Assignee: matz (Yukihiro Matsumoto) Category: core Target version: 2.0.0 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/