生成器

PHP 有一个很好的机制,可以以紧凑的方式创建迭代器。这种迭代器有一些严重的限制:它们只能向前,不能倒转。事实上,即使只是从头开始启动一个迭代器,也必须重建生成器。从本质上说,这是一种只能向前的迭代器。

使用 yield 关键字而不是 return 关键字的函数。它的作用与返回语句相同,但不会停止函数的执行。生成器函数可以任意多次产生数据。

在数组中填充值时,这些值必须存储在内存中,这可能会导致超出 PHP 内存限制或生成器需要大量的处理时间。如果将逻辑放在生成器函数中,则不存在这种开销。生成器函数可以根据需要生成任意数量的结果,而不需要先预置一个数组。

下面是一个简单的生成器,它将 var_dump 一个字符串,说明生成器已启动。然后,函数将生成前五个平方数,同时用 var_dump 输出它们在数列中的位置。最后,它将指示生成器已结束:

<?php

function squaredNumbers()
{
    var_dump("Generator starts.");
    for ($i = 0; $i < 5; ++$i) {
        var_dump($i . " in series.");
        yield pow($i, 2);
    }
    var_dump("Generator ends.");
}

foreach (squaredNumbers() as $number) {
    var_dump($number);
}

该脚本的第二部分循环该函数并对每个数字运行 var_dump 字符串。其输出如下:

image 2023 10 31 09 28 27 857

让我们稍微修改一下这个函数。

非常需要注意的是,如果给变量添加返回类型,则只能声明 GeneratorIteratorTraversableinteger 的返回类型。

下面是代码:

<?php

function squaredNumbers(int $start, int $end): Generator
{
    for ($i = $start; $i <= $end; ++$i) {
        yield pow($i, 2);
    }
}

foreach (squaredNumbers(1, 5) as $number) {
    var_dump($number);
}

结果如下:

image 2023 10 31 09 30 26 745

如果我们想要生成一个键和一个值怎么办? 嗯,这相当容易。

对于那些在 PHP 5 中使用过生成器的人来说,关于生成器还有一些事情需要提及:在 PHP 5 中,当您想要在将变量设置为变量的同时同时生成变量时,必须将 yield 语句括在括号中。 PHP 7 中不存在此限制。

这适用于 PHP 5 和 7:

$data = (yield $value);

这仅适用于 PHP 7:

$data = yield $value;

假设我们想要修改生成器,以便它产生键值结果。代码如下所示:

Unresolved include directive in modules/ROOT/pages/ch05/ch5-04.adoc - include::example$/Chapter 5/Generator/index.php[]

当我们测试这个时,我们将 var_dump 一个二维数组,其中包含生成器在给定迭代中生成的任何键值存储。

下面是输出:

image 2023 10 31 09 33 55 916

还有一些其他提示,没有变量的 Yield 语句(如后续命令中所示)将简单地产生 null

yield;

您还可以使用 yield from 来生成任何给定生成器的内部值。

假设我们有一个包含两个值的数组:

[1, 2]

当我们使用 yield from 产生一个包含两个值的数组时,我们得到了数组的内部值。 让我演示一下:

<?php

function innerGenerator()
{
    yield from [1, 2];
}

foreach (innerGenerator() as $number) {
    var_dump($number);
}

这将显示以下输出:

image 2023 10 31 09 37 26 785

但是,现在让我们更改此脚本,以便它使用 yield 而不是 yield from

<?php

function innerGenerator()
{
    yield [1, 2];
}

foreach (innerGenerator() as $number) {
    var_dump($number);
}

现在我们将看到,我们不仅获得了数组的内部值,还获得了外部容器:

image 2023 10 31 09 39 31 021