PHP中单个循环中的多个生成器

时间:2014-05-04 07:00:31

标签: php generator yield

我需要编写一个简单的脚本来加载来自多个文件的数据并以某种方式合并它。但是,鉴于文件可能非常庞大,我想部分加载数据。为此,我决定使用yield。根据示例,我发现我可以使用以下构造用于单个生成器:

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

但如果我想一次使用两台发电机呢?

$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?
}

6 个答案:

答案 0 :(得分:15)

Generators in PHP实施Iterator interface,因此您可以合并/合并多个Generator,就像您可以合并多个Iterator一样。

如果你想一个接一个地迭代两个生成器(合并A + B),那么你可以使用AppendIterator

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

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

如果您想一次迭代两个生成器,可以使用MultipleIterator

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

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

这两个包含的例子。示例和警告也在我的博客文章中:

否则我不明白你一直在要求什么。如果你能澄清一下,我应该能给你更多指示。

答案 1 :(得分:1)

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

  

通过yield from的生成器委派

     

在PHP 7中,生成器委派允许您从另一个生成值   生成器,Traversable对象或array(通过使用 yield from 关键字)。   然后外部生成器将产生所有值   内部生成器,对象或数组,直到不再有效为止,   之后,执行将在外部生成器中继续进行。

因此可以使用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;
};

或更花哨的东西(在这里,无法使用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();
        }
    }
};

答案 2 :(得分:0)

如果您想将Generators与AppendIterator一起使用,则需要使用NoRewindIterator:

https://3v4l.org/pgiXB

<?php
function foo() {
        foreach ([] as $foo) {
                yield $foo;
        }
}
$append = new AppendIterator();
$append->append(new NoRewindIterator(foo()));

var_dump(iterator_to_array($append));

如果Generator从未实际调用yield,尝试使用AppendIterator遍历裸露的Generator将导致致命错误:

https://3v4l.org/B4Qnh

<?php
function foo() {
        foreach ([] as $foo) {
                yield $foo;
        }
}
$append = new AppendIterator();
$append->append(foo());

var_dump(iterator_to_array($append));

输出:

Fatal error: Uncaught Exception: Cannot traverse an already closed generator in /in/B4Qnh:10
Stack trace:
#0 [internal function]: AppendIterator->rewind()
#1 /in/B4Qnh(10): iterator_to_array(Object(AppendIterator))
#2 {main}
  thrown in /in/B4Qnh on line 10

Process exited with code 255.

答案 3 :(得分:-1)

试试这个:

<?php
foreach($generatorA as $key=>$i) {
    $A=$i;//value from $generatorA
    $B=$generatorB[$key];//value from $generatorB
}

答案 4 :(得分:-1)

我相信您可以先合并两个数组,然后在同一个foreach

中使用它

像:

$generatorA = $someClass1->load(); //load method uses yield so it returns generator object
$generatorB = $someClass2->load(); //load method uses yield so it returns generator object

$result = array_merge($generatorA, $generatorB);
foreach($result as $i) {
  //use as you want
}

详细信息:Array_merge

答案 5 :(得分:-1)

类似的东西:

$generatorA = $someClass1->load(); //load method uses yield so it returns generator object
$generatorB = $someClass2->load(); //load method uses yield so it returns generator object

$flag = true;
$i = 0;
while($flag === false) {

 if ($i >= count($generatorA) || $i >= count($generatorB)) {
      $flag = true;
 }

 // Access both generators
 $genA = $generatorA[$i];
 $genB = $generatorB[$i];

$i++;
}