php中的生成器函数 yield
生成器函数和普通函数很类似,不同的是生成器函数有个关键词 yield 。如果一个函数中使用了 yield ,这个函数就是一个生成器函数。
生成器函数返回的是一个可以被遍历的对象
意思是返回的是一个可以被遍历的对象。每次遍历可以获取一个yield 的返回值, 一个生成器函数可以有多个 yield 。
yield 一个例子
function test_yield(){
echo "c1" . PHP_EOL;
yield 1;
yield 2;
echo "c2" .PHP_EOL;
yield 3;
yield 4;
}
$result = test_yield(); // 这里调用上面我们创建的函数
echo "[" . date("H:i:s") ."]" . PHP_EOL;
foreach($result as $value){
sleep(1);//这里停顿1秒,我们后续有用
echo $value . "[" . date("H:i:s") ."]" . PHP_EOL;
}
输出的值
[05:58:30]
c1
1[05:58:31]
2[05:58:32]
c2
3[05:58:33]
4[05:58:34]
可以看出每次遍历输出一个yield 的生成值。在 foreach 中一共输出了 4 个。
https://www.php.net/manual/zh/language.generators.overview.php
生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是, 生成器可以根据需要 yield 多次,以便生成需要迭代的值。一个简单的例子就是使用生成器来重新实现 range() 函数。 标准的 range() 函数需要在内存中生成一个数组包含每一个在它范围内的值,然后返回该数组, 结果就是会产生多个很大的数组。 比如,调用 range(0, 1000000) 将导致内存占用超过 100 MB。做为一种替代方法, 我们可以实现一个 xrange() 生成器, 只需要足够的内存来创建 Iterator 对象并在内部跟踪生成器的当前状态,这样只需要不到1K字节的内存。
https://www.php.net/manual/zh/language.generators.syntax.php
生成器函数看起来像普通函数——不同的是普通函数返回一个值,而生成器可以 yield 生成多个想要的值。 任何包含 yield 的函数都是一个生成器函数。当一个生成器被调用的时候,它返回一个可以被遍历的对象.当你遍历这个对象的时候(例如通过一个foreach循环),PHP 将会在每次需要值的时候调用对象的遍历方法,并在产生一个值之后保存生成器的状态,这样它就可以在需要产生下一个值的时候恢复调用状态。一旦不再需要产生更多的值,生成器可以简单退出,而调用生成器的代码还可以继续执行,就像一个数组已经被遍历完了。
示例: 使用更少的内存
function test($n = 10) {
$arr = [];
for($i = 0; $i < $n ; $i++) {
$arr[$i] = $i;
}
return $arr;
}
function test2($n = 10) {
for($i = 0; $i < $n ; $i++) {
yield $i;
}
}
/*
* 会导致内存不足
foreach( test(PHP_INT_MAX) as $ans ) {
echo $ans;
}
*/
foreach( test2(PHP_INT_MAX) as $ans ) {
echo $ans;
}
读取文件的示例
function readlog ($filename) {
$file = fopen($filename, 'rb');
echo "-- start " . PHP_EOL;
# feof 错误或者到文件结尾 返回 true
while (feof($file) === false ) {
yield fgets($file);
}
echo "-- start " . PHP_EOL;
fclose($file);
}
foreach ( readlog("web.log") as $line => $value ) {
echo " $line - " . $value;
if($line >= 2 ) {
// !!! 没有执行完,会导致 echo "-- start " . PHP_EOL; fclose($file); 没有运行
break;
}
}