PHP yield 关键字
最后修改于 2025 年 4 月 16 日
PHP 的 yield 关键字创建生成器函数,这些函数可以暂停执行并一次返回一个值。生成器提供内存高效的迭代大型数据集,而无需构建数组。它们在处理数据序列和流时非常强大。
基本定义
生成器函数是一种特殊的函数,它使用 yield 而不是 return。调用时,它会返回一个可以迭代的 Generator 对象。每次迭代都会从上一个 yield 恢复执行。
yield 关键字既为迭代器提供了值,又暂停了函数执行。当请求下一个值时,执行会恢复。这使得能够内存高效地处理大型数据集。
生成器实现了 Iterator 接口,因此它们可以与 foreach 循环一起使用。它们在 yield 之间维护其状态,包括局部变量的值。生成器可以在迭代期间同时 yield 和接收值。
基本生成器函数
此示例演示了一个简单的生成器函数,该函数 yield 三个值。
<?php
declare(strict_types=1);
function numberGenerator() {
yield 1;
yield 2;
yield 3;
}
foreach (numberGenerator() as $number) {
echo $number . "\n";
}
numberGenerator 函数按顺序 yield 三个数字。调用时,它会返回一个可以迭代的 Generator 对象。每次 yield 都会暂停执行,直到下一个迭代。foreach 循环一次消耗一个值。
生成范围
此示例显示了如何创建内存高效的范围生成器。
<?php
declare(strict_types=1);
function xrange($start, $limit, $step = 1) {
for ($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
}
foreach (xrange(1, 1000000) as $number) {
echo $number . "\n";
}
xrange 生成器在不创建数组的情况下生成从 start 到 limit 的数字。无论范围大小如何,它都使用恒定的内存。这与 range() 不同,后者会在内存中构建整个数组。生成器在大型序列方面表现出色。
Yield 键值对
此示例演示了从生成器 yield 关联键值对。
<?php
declare(strict_types=1);
function userGenerator() {
yield 'id' => 1;
yield 'name' => 'John Doe';
yield 'email' => 'john@example.com';
}
foreach (userGenerator() as $key => $value) {
echo "$key: $value\n";
}
生成器 yield 键值对,这些键值对可以在 foreach 循环中消耗。此语法类似于数组迭代。每次 yield 都同时提供键和值。生成器可以在不存储所有元素的情况下模拟关联数组。
接收值
此示例显示了生成器如何在迭代期间接收值。
<?php
declare(strict_types=1);
function echoingGenerator() {
echo "Generator started\n";
$input = yield;
echo "Received: $input\n";
$input = yield;
echo "Received: $input\n";
}
$gen = echoingGenerator();
$gen->send('First');
$gen->send('Second');
生成器在首次调用时启动。send 方法向生成器提供值。每次 send 都会恢复执行,直到下一个 yield。值成为 yield 表达式的结果。这使得能够进行双向通信。
内存效率
此示例比较了数组方法和生成器方法之间的内存使用情况。
<?php
declare(strict_types=1);
function generateLines($file) {
$handle = fopen($file, 'r');
while (!feof($handle)) {
yield fgets($handle);
}
fclose($handle);
}
// Array approach would load entire file into memory
foreach (generateLines('large_file.txt') as $line) {
// Process each line
}
生成器一次读取一行,使用最少的内存。数组方法会一次性加载整个文件。生成器非常适合大型数据集。内存使用量与输入大小无关,保持恒定。这使得能够处理大于可用内存的文件。
无限序列
此示例演示了如何使用生成器创建无限序列。
<?php
declare(strict_types=1);
function fibonacci() {
$a = 0;
$b = 1;
while (true) {
yield $a;
[$a, $b] = [$b, $a + $b];
}
}
$count = 0;
foreach (fibonacci() as $number) {
echo $number . "\n";
if (++$count > 10) break;
}
斐波那契生成器创建了一个没有内存问题的无限序列。while(true) 循环对于数组来说很危险,但对于 yield 来说是安全的。消费者控制何时停止迭代。这种模式对于数学序列和流很有用。
生成器委托
此示例展示了如何使用 yield from 委托给另一个生成器。
<?php
declare(strict_types=1);
function countToThree() {
yield 1;
yield 2;
yield 3;
}
function countToFive() {
yield from countToThree();
yield 4;
yield 5;
}
foreach (countToFive() as $number) {
echo $number . "\n";
}
yield from 语法委托给另一个生成器。来自内部生成器的值直接 yield 给调用者。这使得生成器组合和代码重用成为可能。委托完成后,执行会返回到外部生成器。
最佳实践
- 内存:对大型数据集使用生成器以节省内存。
- 性能:生成器在单次迭代中速度很快。
- 可重用性:生成器结果无法倒带或重用。
- 清晰度:为其他开发人员记录生成器行为。
- 兼容性:与所有 PHP 迭代构造一起使用。
来源
本教程涵盖了 PHP 生成器,并通过实际示例展示了 yield 的用法,用于内存高效迭代、无限序列和数据处理。
作者
列出 所有 PHP 教程。