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

5jvtdoz2  于 2022-12-02  发布在  PHP
关注(0)|答案(7)|浏览(199)

我需要写一个简单的脚本,从多个文件中加载数据,并以某种方式合并它们。然而,考虑到文件可能相当大,我想部分加载数据。为此,我决定使用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?
}
gv8xihay

gv8xihay1#

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)) {
    ...

这两个例子,包括例子和警告,都在我的这个博客帖子中:

否则我不明白你一直在问什么。如果你能澄清,我应该可以给予你更多的方向。

6ss1mwsb

6ss1mwsb2#

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

通过yield from进行的生成器委派

在PHP 7中,生成器委托允许您使用**yield from**关键字从另一个生成器、Traversable对象或array中生成值。然后外部生成器将从内部生成器、对象或数组中生成所有值,直到这些值不再有效,然后在外部生成器中继续执行。
因此,可以使用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();
        }
    }
};
z2acfund

z2acfund3#

虽然AppendIterator适用于Iterators,但它存在一些问题。
首先,需要构造一个新的对象而不是调用一个函数是不太好的,更不好的是,你需要改变AppendIterator,因为你不能在它的构造函数中提供内部迭代器。
其次,AppendIterator只接受Iterator示例,所以如果你有一个Traversable,比如IteratorAggregate,你就不走运了。
这个PHP 7.1+函数结合了两个iterable

/**
 * array_merge clone for iterables using lazy evaluation
 *
 * As with array_merge, numeric elements with keys are assigned a fresh key,
 * starting with key 0. Unlike array_merge, elements with duplicate non-numeric
 * keys are kept in the Generator. Beware that when converting the Generator
 * to an array with a function such as iterator_to_array, these duplicates will
 * be dropped, resulting in identical behaviour as array_merge.
 *
 *
 * @param iterable ...$iterables
 * @return Generator
 */
function iterable_merge( iterable ...$iterables ): Generator {
    $numericIndex = 0;

    foreach ( $iterables as $iterable ) {
        foreach ( $iterable as $key => $value ) {
            yield is_int( $key ) ? $numericIndex++ : $key => $value;
        }
    }
}

用法示例:

foreach ( iterable_merge( $iterator1, $iterator2, $someArray ) as $k => $v ) {}

此函数是small library for working with iterable的一部分,在small library for working with iterable中也进行了广泛的测试。

xxb16uws

xxb16uws4#

如果要将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.
xienkqul

xienkqul5#

您可以使用来自

function one()
{ 

   yield 1;
   yield 2;

}

function two()
{
   yield 3;
   yield 4;
}

function merge()
{
   yield from one();
   yield from two();

}
foreach(merge() as $i)
{
   echo $i;
}

可重用函数示例

function iterable_merge( iterable ...$iterables ): Generator {
   

    foreach ( $iterables as $iterable ) {
        
        yield from $iterable;
    }
}

$merge=iterable_merge(one(),two());
stszievb

stszievb6#

类似于:

$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++;
}
sh7euo9m

sh7euo9m7#

试试这个:

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

相关问题