在PHP中,收益是什么意思?

我最近偶然发现了这个代码:

function xrange($min, $max) { for ($i = $min; $i <= $max; $i++) { yield $i; } } 

我从来没有见过这个yield关键字。 试图运行我得到的代码

parsing错误:语法错误,第x行出现意外的T_VARIABLE

那么这个yield关键字是什么? 它甚至是有效的PHP? 如果是这样,我该如何使用它?

什么是yield

yield关键字从一个生成器函数返回数据:

生成器函数的核心是yield关键字。 在最简单的forms中,yield语句看起来很像是一个return语句,除了不是停止函数的执行和返回,而是为代码循环遍历生成器并暂停执行生成器函数提供一个值。

什么是发电机function?

生成器函数实际上是编写迭代器的更加紧凑和高效的方式。 它允许你定义一个函数(你的xrange ),它将在循环 计算并返回值:

 foreach (xrange(1, 10) as $key => $value) { echo "$key => $value", PHP_EOL; } 

这将创build以下输出:

 0 => 1 1 => 2 … 9 => 10 

你也可以通过使用foreach来控制$key

 yield $someKey => $someValue; 

在生成器函数中, $someKey是您想要为$key显示的任何内容, $someValue$val的值。 在问题的例子是$i

正常function有什么不同?

现在你可能想知道为什么我们不是简单地使用PHP的本地range函数来实现这个输出。 对,你是。 输出将是相同的。 不同的是我们如何到达那里。

当我们使用range PHP时,将执行它,在内存中创build整个数组数组,然后return 整个数组 returnforeach循环,然后再遍历它并输出值。 换句话说, foreach将在数组本身上运行。 rangefunction和foreach只“说”一次。 想想就像在邮件中获得一个包一样。 送货员将把包裹交给你,然后离开。 然后你打开整个包裹,拿出里面的东西。

当我们使用生成器函数时,PHP将进入函数并执行它,直到它满足结束或yield关键字。 当它满足yield ,它将会把当时的价值返回到外部循环。 然后它回到发电机function,并继续从它产生的地方。 由于你的xrange持有一个for循环,它会执行并产生,直到达到$max 。 把它想象成是玩弄乒乓球的foreach和发电机。

我为什么需要这个?

显然,生成器可以用来解决内存限制。 根据你的环境,做一个range(1, 1000000)会致命的脚本,而同样的发电机将正常工作。 或者如维基百科所说:

因为发生器只根据需要计算出它们的屈服值,所以它们对于表示一次很昂贵或不可能计算的序列是有用的。 这些包括例如无限序列和实时数据stream。

发电机也应该是相当快的。 但请记住,当我们谈论得很快的时候,我们通常只会谈论很less的话题。 所以,在你现在跑掉之前,改变所有的代码来使用生成器,做一个基准来看看它的意义。

发生器的另一个用例是asynchronous协程。 yield关键字不仅返回值,还接受它们。 有关详细信息,请参阅下面链接的两个优秀的博客文章。

从什么时候我可以使用yield

生成器已经在PHP 5.5中引入。 尝试在该版本之前使用yield会导致各种parsing错误,具体取决于关键字后面的代码。 所以,如果你从那个代码得到一个分析错误,更新你的PHP。

来源和进一步阅读:

  • 官方文档
  • 原来的RFC
  • kelunik的博客:介绍发电机
  • ircmaxell的博客:什么发电机可以为你做
  • NikiC的博客:在PHP中使用协程的协同多任务处理
  • 合作PHP多任务处理
  • 发生器和数组有什么区别?
  • 一般的发电机维基百科

yield关键字用于定义PHP 5.5中的“生成器”。 好的,那么什么是发电机 ?

从php.net:

生成器提供了一种简单的方法来实现简单的迭代器,而无需实现实现Iterator接口的类的开销或复杂性。

一个生成器允许你编写使用foreach遍历一组数据的代码,而不需要在内存中构build一个数组,这可能会导致你超出内存限制,或者需要大量的处理时间来生成。 相反,您可以编写一个生成器函数,它与普通函数相同,除了不是一次返回,生成器可以根据需要产生多次,以提供要迭代的值。

从这个地方:发电机=发电机,其他function(只是一个简单的function)=function。

所以,他们是有用的时候:

  • 你需要做简单的事情(或简单的事情);

    生成器真的非常简单,然后实现Iterator接口。 另一方面是发电机的function不足。 比较它们 。

  • 您需要生成大量的数据 – 节省内存;

    实际上为了节省内存,我们可以通过函数为每个循环迭代生成所需的数据,迭代后使用垃圾。 所以这里的要点是 – 清晰的代码和可能的性能。 看看什么更适合你的需求。

  • 你需要生成序列,这取决于中间值;

    这是以前的想法的延伸。 发电机可以使事情比function更容易。 检查斐波那契的例子 ,并尝试做序列没有生成器。 在这种情况下,发生器也可以更快地工作,至less是因为将中间值存储在局部variables中;

  • 你需要提高性能。

    他们可以在某些情况下工作得更快,然后运作(见前面的好处);

简单的例子

 <?php echo '#start main# '; function a(){ echo '{start['; for($i=1; $i<=9; $i++) yield $i; echo ']end} '; } foreach(a() as $v) echo $v.','; echo '#end main#'; ?> 

产量

 #start main# {start[1,2,3,4,5,6,7,8,9,]end} #end main# 

使用yield可以很容易地在单个函数中描述多个任务之间的断点。 就是这样,没有什么特别的。

 $closure = function ($injected1, $injected2, ...){ $returned = array(); //task1 on $injected1 $returned[] = $returned1; //I need a breakpoint here!!!!!!!!!!!!!!!!!!!!!!!!! //task2 on $injected2 $returned[] = $returned2; //... return $returned; }; $returned = $closure($injected1, $injected2, ...); 

如果task1和task2是高度相关的,但是你需要在它们之间有一个断点来做别的事情:

  • 处理数据库行之间的空闲内存
  • 运行其他任务,这些任务为下一个任务提供依赖性,但是通过理解当前代码,这些任务是不相关的
  • 进行asynchronous调用并等待结果
  • 等等 …

那么生成器是最好的解决scheme,因为您不必将代码拆分为多个闭包,或者将其与其他代码混合,或者使用callback等。您只需使用yield来添加断点,然后就可以继续如果你准备好了那个断点。

添加没有生成器的断点:

 $closure1 = function ($injected1){ //task1 on $injected1 return $returned1; }; $closure2 = function ($injected2){ //task2 on $injected2 return $returned1; }; //... $returned1 = $closure1($injected1); //breakpoint between task1 and task2 $returned2 = $closure2($injected2); //... 

添加断点与发电机

 $closure = function (){ $injected1 = yield; //task1 on $injected1 $injected2 = (yield($returned1)); //task2 on $injected2 $injected3 = (yield($returned2)); //... yield($returnedN); }; $generator = $closure(); $returned1 = $generator->send($injected1); //breakpoint between task1 and task2 $returned2 = $generator->send($injected2); //... $returnedN = $generator->send($injectedN); 

注意:发生器很容易出错,所以在执行之前,一定要写unit testing! 注2:在无限循环中使用生成器就像写一个无限长的闭包…

这个函数使用yield:

 function a($items) { foreach ($items as $item) { yield $item + 1; } } 

几乎是一样的这个没有:

 function b($items) { $result = []; foreach ($items as $item) { $result[] = $item + 1; } return $result; } 

只有一个区别, a()返回一个生成器和b()只是一个简单的数组。 但是,您可以在两者上进行迭代,这是重要的方面。