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)
=> #<Enumerator: "abcde":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/