今天在闲逛PHP的官方文档的时候,发现了这么个用法:yield,叫 生成器 官方是这么描述的
(PHP 5 >= 5.5.0, PHP 7) 生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。 生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是, 生成器可以根据需要 yield 多次,以便生成需要迭代的值。 一个简单的例子就是使用生成器来重新实现 range() 函数。 标准的 range() 函数需要在内存中生成一个数组包含每一个在它范围内的值,然后返回该数组, 结果就是会产生多个很大的数组。 比如,调用 range(0, 1000000) 将导致内存占用超过 100 MB。 做为一种替代方法, 我们可以实现一个 xrange() 生成器, 只需要足够的内存来创建 Iterator 对象并在内部跟踪生成器的当前状态,这样只需要不到1K字节的内存。
果然还是完全看不懂在说什么。看下官方的示例代码吧:
/* * for the protection from the leaking of resources * see RFC https://wiki.php.net/rfc/generators#closing_a_generator * and use finnaly */ //sample code function getLines($file) { $f = fopen($file, 'r'); try { while ($line = fgets($f)) { yield $line; } } finally { fclose($f); } } foreach (getLines("file.txt") as $n => $line) { if ($n > 5) break; echo $line; }简而言之呢,就是如果在某个函数中使用了生成器yield那么这个这个函数会被推迟到实际被foreach语句迭代的时候才会被调用。 官方的描述是会大量节省内存,提高内存。我心想这是神器啊!但是转念一想,如果真的这么好用,为啥却没有被大规模使用呢?是出反常必有妖。
下面测试一下:
传统写法如下:
function test() { $data = []; for ($i = 0; $i < 1000000; $i++) { //echo "执行了一次"; $data[] = $i; } } foreach (test() as $value) { var_dump($value); //echo "<br>"; }然后看运行时间和内存占用是:
运行时间为:0.0513消耗内存为:0.7968KB使用生成器的写法:
function test() { for ($i = 0; $i < 1000000; $i++) { //echo "执行了一次"; yield $i; } } foreach (test() as $value) { var_dump($value); //echo "<br>"; }再看下运行的时间与消耗的内存:
运行时间为:0.5249消耗内存为:0.1093KBemmmm… 传统写法因为会一次性先把数据存入内存,所以占用内存比较大。而yield的写法,则可以有效节约内存。但是相对应的代价就是:时间消耗大约是传统写法的十倍
看来yield只适合处理较大数据例如数G的表格之类的,明显对内存造成了过大的压力情况下才去使用。在数据量比较小的情况下,还是不要使用了。至于背后的原理,则不是本身讨论的内容了(我不会说是因为我也不知道)。