Hi,

On 18 August 2010 22:02, Marc-Andre Lafortune <redmine / ruby-lang.org> wrote=
:
> It would be useful to be able to ask an Enumerator for the number of time=
s it will yield, without having to actually iterate it. [...]
> This would print out "Progress: 1 / 20", etc..., while doing the stuff.

That seems cool, indeed.

> *** Proposed changes ***
>
> * Enumerator#size *
>
> call-seq:
> =A0e.size =A0 =A0 =A0 =A0 =A0-> int, Float::INFINITY or nil
> =A0e.size {block} =A0-> int
>
> Returns the size of the enumerator.
> The form with no block given will do a lazy evaluation of the size withou=
t going through the enumeration. If the size can not be determined then +ni=
l+ is returned.

Interesting, though I do not feel INFINITY to be the right answer if
it is a loop (but yes, it should obviously be bigger than anything you
compare to).

> The form with a block will always iterate through the enumerator and retu=
rn the number of times it yielded.
>
> =A0(1..100).to_a.permutation(4).size # =3D> 94109400
> =A0loop.size # =3D> Float::INFINITY
>
> =A0a =3D [1, 2, 3]
> =A0a.keep_if.size =A0 =A0 =A0 =A0 # =3D> 3
> =A0a =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# =3D> [1, 2, 3]
> =A0a.keep_if.size{false} =A0# =3D> 3
> =A0a =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0# =3D> []
>
> =A0[1, 2, 3].drop_while.size =A0 =A0 =A0 =A0 =A0 =A0 # =3D> nil
> =A0[1, 2, 3].drop_while.size{|i| i < 3} =A0# =3D> 2

What is the interest compared to Enumerable#count ? (also, #size with
a block does not feel natural)

> a =3D [1, 2, 3]
> a.keep_if.count # =3D> 3
> a # =3D> []

Here the result is identical.

> [1, 2, 3].drop_while.count # =3D> 1

Here it is different, and your method behaves strangely.
The block is yielded 3 times, but your method returns 2 ?

And if you wanted the size of the resulting Array, it seem more obvious to =
do:
[1,2,3].drop_while { |i| i < 3 }.size # =3D> 1

> * Enumerator#size=3D *

Having a mutator on Enumerator does not make sense to me, as
previously mentioned by others.
--

However, it is really interesting to know the times an enumerator will yiel=
d.

I believe checking for nil is really not beautiful in the code, so I
think it would be nicer if it actually use the block form if it can
not be determined directly.
But this could introduce unwanted side-effects (the #drop_while is an examp=
le).
These side-effects should be known, and I do not see any real use for
destructive method with #size, do you ?

About infinite enumerator, it should just yield forever (so calling #each).

I would then propose that Enumerable#count (without block) use this
code to give directly the times it would yield, or if it can not, do
the real iteration like now.

--
And this seems to be already supposed in the documentation of #count:
  Returns the number of items in enum, where #size is called if it
  responds to it, otherwise the items are counted through enumeration.

So, these enumerators should just provide #size, and it should work.

However, it does not behave like that:
> a =3D (1..1000000000000).to_enum # =3D> #<Enumerator: 1..1000000000000:ea=
ch>
> def a.size; 3 end
> a.size # =3D> 3
> a.count # =3D> take ages
Defining on the class (Range) does not work either.

So it seems to be a problem with the implementation (in enum.c, line
~122): it does not check if it #respond_to?(:size):
It calls #each with count_all_i() as a block, because there is no
block and no argument.
count_all_i() being a simple block which just increment a counter (memo).

I believe Enumerable#count should respect its documentation, and that
your work would help to implement #size on the Enumerator which sizes
can be known it immediately. Then we would have a consistent behavior,
already described in the documentation !

What do you think ?

Regards,
B.D.