Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple generators in a single loop within PHP

I need to write a simple script that loads data from multiple files and merges it somehow. However, given the fact that the files might be quite huge I'd like to load data partially. To do so I decided to use yield. And according to examples I found I could use following construction for single generator:

$generator = $someClass->load(); //load method uses yield so it returns generator object
foreach($generator as $i) {
  // do something
}

But what if I want to use two generators at once?

$generatorA = $someClass1->load(); //load method uses yield so it returns generator object
$generatorB = $someClass2->load(); //load method uses yield so it returns generator object
foreach($generatorA as $i) {
  // how can I access to resultSet from generatorB here?
}
like image 430
Tomasz Kapłoński Avatar asked May 04 '14 07:05

Tomasz Kapłoński


2 Answers

From https://www.php.net/manual/en/language.generators.syntax.php#control-structures.yield.from

Generator delegation via yield from

In PHP 7, generator delegation allows you to yield values from another generator, Traversable object, or array by using the yield from keyword. The outer generator will then yield all values from the inner generator, object, or array until that is no longer valid, after which execution will continue in the outer generator.

So it's possible to combine two (or more) generators using yield from.

/**
  * Yield all values from $generator1, then all values from $generator2
  * Keys are preserved
  */
function combine_sequentially(Generator $generator1, Generator $generator2): Generator
{
    yield from $generator1;
    yield from $generator2;
};

Or something more fancy (here, it's not possible to use yield from):

/**
  * Yield a value from $generator1, then a value from $generator2, and so on
  * Keys are preserved
  */
function combine_alternatively(Generator $generator1, Generator $generator2): Generator
{
    while ($generator1->valid() || $generator2->valid()) {
        if ($generator1->valid()) {
            yield $generator1->key() => $generator1->current();
            $generator1->next();
        }
        if ($generator2->valid()) {
            yield $generator2->key() => $generator2->current();
            $generator2->next();
        }
    }
};
like image 45
jacek.ciach Avatar answered Nov 15 '22 15:11

jacek.ciach


Generators in PHP implement the Iterator interface, so you can merge / combine multiple Generators like you can combine multiple Iterators.

If you want to iterate over both generators one after the other (merge A + B), then you can make use of the AppendIterator.

$aAndB = new AppendIterator();
$aAndB->append($generatorA);
$aAndB->append($generatorB);

foreach ($aAndB as $i) {
    ...

If you want to iterate over both generator at once, you can make use of MultipleIterator.

$both = new MultipleIterator();
$both->attachIterator($generatorA);
$both->attachIterator($generatorB);

foreach ($both as list($valueA, $valueB)) {
    ...

Example for those two incl. examples and caveats are in this blog-post of mine as well:

  • Iterating over Multiple Iterators at Once

Otherwise I don't understand what you've been asking for. If you could clarify, I should be able to give you more directions.

like image 160
hakre Avatar answered Nov 15 '22 15:11

hakre