JLT93 JLT93 - 26 days ago 7
PHP Question

PHP - How to count a generators yields

Using PHP >= 5.5 if we have a method that yielded values, what would be the best method in counting these values?

What I was expecting was to be able to convert a Generator to an array and count that, however it would return an empty array. Count() also does not work as the Generator is reported as empty despite not being empty.

I'm baffled with this. If you don't need to count a generators yields then it's a nice feature otherwise I don't see much of a point for it. There is a way to detect if a generator is empty, this is by using the key() method and if it returns NULL then there are either no yields or the generator has already been iterated through which would mean the current pointer is null.

Answer

You should understand, that generator isn't data structure - it's an instance of Generator class and, actually, it's special sort of Iterator. Thus, you can't count its items directly (to be precise - that's because Generator class implements only Iterator interface, and not Countable interface. To be honest, I can't imagine how can it implement that)

To count values with native generator you'll have to iterate through it. But that can not be done in common sense - because in most cases it's you who'll decide how many items will be yielded. Famous xrange() sample from manual:

function xrange($start, $limit, $step = 1) {
    if ($start < $limit) {
        if ($step <= 0) {
            throw new LogicException('Step must be +ve');
        }

        for ($i = $start; $i <= $limit; $i += $step) {
            yield $i;
        }
    } else {
        if ($step >= 0) {
            throw new LogicException('Step must be -ve');
        }

        for ($i = $start; $i >= $limit; $i += $step) {
            yield $i;
        }
    }
}

-as you can see, it's you who must define borders. And final count will depend from that. Iterating through generator will have sense only with static-borders defined generator (i.e. when count of items is always static - for example, defined inside generator strictly). In any other case you'll get parameter-dependent result. For xrange():

function getCount(Generator $functor)
{
   $count = 0;
   foreach($functor as $value)
   {
      $count++;
   }
   return $count;
}

-and usage:

var_dump(getCount(xrange(1, 100, 10)));//10
var_dump(getCount(xrange(1, 100, 1)));//100

-as you can see, "count" will change. Even worse, generator hasn't to be finite. It may yield infinite set of values (and borders are defined in external loop, for example) - and this is one more reason which makes "counting" near senseless.