带你详解PHP生成器的运用
什么是生成器?
听着高峻上的名字,感受像是制造什么东西的一个功效,实际上,生成器是一个用于迭代的迭代器。它供给了一种更容易的方式来实现简便的对象迭代,比拟较定义类实现Iterator接口的方式,机能开销和复杂性大大落低。
引荐:《PHP视频教程》
说了半天不如直接看看代码更直不雅。
function test1() { for ($i = 0; $i < 3; $i++) { yield $i + 1; } yield 1000; yield 1001; } foreach (test1() as $t) { echo $t, PHP_EOL; } // 1 // 2 // 3 // 1000 // 1001
就是这么简便的一段代码。第一,生成器必需在办法中并使用 yield 关键字;其次,每一个 yield 可以看作是一次 return ;最后,外部轮回时,一次轮回取一个 yield 的返回值。在这个例子,轮回三次返回了1、2、3这三个数字。然后在轮回外部又写了两行 yield 离别输出了1000和1001。因此,外部的 foreach 一共轮回输出了五次。
很奇妙吧,明明是一个办法,为什么能够轮回它并且还是很惊奇的一种返回轮回体的格局。我们直接打印这个 test() 办法看看打印的是啥:
// 是一个生成器对象 var_dump(test1()); // Generator Object // ( // )
当使用了 yield 停止内容返回后,返回的是一个 Generator 对象。这个对象就叫作生成器对象,它不克不及直接被 new 实例化,只能通过生成器函数这种方式返回。这个类包括 current() 、 key() 等办法,并且最主要的这个类实现了 Iterator 接口,所以,它就是一个非凡的迭代器类。
Generator implements Iterator { /* 办法 */ public current ( void ) : mixed public key ( void ) : mixed public next ( void ) : void public rewind ( void ) : void public send ( mixed $value ) : mixed public throw ( Exception $exception ) : void public valid ( void ) : bool public __wakeup ( void ) : void }
生成器有什么用?
搞了半天不就是个迭代器嘛?搞这么费事干嘛,直接用迭代器或者在办法中直接返回一个数组不就好了吗?没错,正常状况下真的没有这么费事,但是假如是在数据量特殊大的状况下,这个生成器就能发挥它的强大威力了。生成器最最强大的部分就在于,它不需要一个数组或者任何的数据构造来留存这一系列数据。每次迭代都是代码施行到 yield 时动态返回的。因此,生成器能够极大的节省内存。
// 内存占用测试 $start_time = microtime(true); function test2($clear = false) { $arr = []; if($clear){ $arr = null; return; } for ($i = 0; $i < 1000000; $i++) { $arr[] = $i + 1; } return $arr; } $array = test2(); foreach ($array as $val) { } $end_time = microtime(true); echo "time: ", bcsub($end_time, $start_time, 4), PHP_EOL; echo "memory (byte): ", memory_get_usage(true), PHP_EOL; // time: 0.0513 // memory (byte): 35655680 $start_time = microtime(true); function test3() { for ($i = 0; $i < 1000000; $i++) { yield $i + 1; } } $array = test3(); foreach ($array as $val) { } $end_time = microtime(true); echo "time: ", bcsub($end_time, $start_time, 4), PHP_EOL; echo "memory (byte): ", memory_get_usage(true), PHP_EOL; // time: 0.0517 // memory (byte): 2097152
上述代码只是简便的停止 1000000 个轮回后猎取结果,不外也可以直不雅地看出。使用生成器的版本仅仅耗损了 2M 的内存,而未使用生成器的版本则耗损了 35M 的内存,直接已经10多倍的差距了,并且越大的量差距超明显。因此,有大神将生成器说成是PHP中最被低估了的一个特性。
生成器的利用
接下来我们来看看生成器的一些根本的利用方式。
返回空值乃至中止
生成器当然也可以返回空值,直接 yield; 不带任何值就可以返回一个空值了。而在办法中直接使用 return; 也可以用来中止生成器的连续施行。下面的代码我们在 $i = 4; 的时候返回的是个空值,也就是不会输出 5 (由于我们返回的是 $i + 1 )。然后在 $i == 7 的时候
使用 return; 中止生成器的连续施行,也就是轮回最多只会输出到 7 就完毕了。
// 返回空值乃至中止 function test4() { for ($i = 0; $i < 10; $i++) { if ($i == 4) { yield; // 返回null值 } if ($i == 7) { return; // 中止生成器施行 } yield $i + 1; } } foreach (test4() as $t) { echo $t, PHP_EOL; } // 1 // 2 // 3 // 4 // 5 // 6 // 7
返回键值对情势
不要惊奇,生成器真的是可以返回键值对情势的可遍历对象供 foreach 使用的,并且语法非常好记: yield key => value; 是不是和数组项的定义情势千篇一律,非常直不雅好懂得。
function test5() { for ($i = 0; $i < 10; $i++) { yield 'key.' . $i => $i + 1; } } foreach (test5() as $k=>$t) { echo $k . ':' . $t, PHP_EOL; } // key.0:1 // key.1:2 // key.2:3 // key.3:4 // key.4:5 // key.5:6 // key.6:7 // key.7:8 // key.8:9 // key.9:10
外部传递数据
我们可以通过 Generator::send 办法来向生成器中传入一个值。传入的这个值将会被当做生成器当前 yield 的返回值。然后我们按照这个值可以做一些推断,比方按照外部前提中止生成器的施行。
function test6() { for ($i = 0; $i < 10; $i++) { // 正常猎取轮回值,当外部send过来值后,yield猎取到的就是外部传来的值了 $data = (yield $i + 1); if($data == 'stop'){ return; } } } $t6 = test6(); foreach($t6 as $t){ if($t == 3){ $t6->send('stop'); } echo $t, PHP_EOL; } // 1 // 2 // 3
上述代码懂得起来大概比力绕,但是留意记住注释的那行话就行了(正常猎取轮回值,当外部send过来值后,yield猎取到的就是外部传来的值了)。别的,变量猎取 yield 的值,必需要用括号括起来。
yield from 语法
yield from 语法其实就是指的从另一个可迭代对象中一个一个的猎取数据并构成生成器返回。直接看代码。
function test7() { yield from [1, 2, 3, 4]; yield from new ArrayIterator([5, 6]); yield from test1(); } foreach (test7() as $t) { echo 'test7:', $t, PHP_EOL; } // test7:1 // test7:2 // test7:3 // test7:4 // test7:5 // test7:6 // test7:1 // test7:2 // test7:3 // test7:1000
在 test7() 办法中,我们使用 yield from 离别从一般数组、迭代器对象、另一个生成器中猎取数据并做为当前生成器的内容停止返回。
小欣喜
生成器可以用count猎取数目吗?
抱愧,生成器是不克不及用count来猎取它的数目的。
$c = count(test1()); // Warning: count(): Parameter must be an array or an object that implements Countable // echo $c, PHP_EOL;
使用 count 来猎取生成器的数目将直接报 Warning 警告。直接输出将会不断显示是 1 ,由于 count 的特性(强迫转换成数组都会显示 1 )。
使用生产器来猎取斐波那契数列
// 利用生成器生成斐波那契数列 function fibonacci($item) { $a = 0; $b = 1; for ($i = 0; $i < $item; $i++) { yield $a; $a = $b - $a; $b = $a + $b; } } $fibo = fibonacci(10); foreach ($fibo as $value) { echo "$value\n"; }
这段代码就不多说明了,非常直不雅的一段代码了。
总结
生成器绝对是PHP中的一个潜藏的宝藏,不仅是关于内存节省来说,并且语法其实也非常的简约明了。我们不需要在办法内部再多定义一个数组去储备返回值,直接 yield 一项一项的返回就可以了。在实际的项目中完全值得尝试一把,但是尝试完了别忘了和小伙伴们分享,大部分人大概真的没有接触过这个特性哦!!
测试代码: https://github.com/zhangyue0503/dev-blog/blob/master/php/202002/source/%E5%AD%A6%E4%B9%A0PHP%E7%94%9F%E6%88%90%E5%99%A8%E7%9A%84%E4%BD%BF%E7%94%A8.php
参照 文档: https://www.php.net/manual/zh/language.generators.overview.php https://www.php.net/manual/zh/class.generator.php
以上就是带你详解PHP生成器的使用的具体内容,更多请关注百分百源码网其它相关文章!