What is PHP’s yield (1)

  coroutine, php, yield

In fact, I didn’t know yield because of iteration or generator or studying PHP manual. If it weren’t for synergetic process, I still didn’t know there was such a ghost thing as yield in PHP. The family has introduced this stuff since PHP 5.5. The official name is generator. You have to say why things from the 1950s are only being taken out now. I also want to ask you, PHP 5.3 has a namespace for Mao, which has only been officially put into production in recent years.

So, the question is, what is the use of this thing?

Let’s feel a problem first, give you 100Kb of memory (yes, you are not wrong, it is 100Kb), and then let you iteratively output an array from 1 to 10000, with a step of 1.

The more you iterate the array, the first you must create the array.

Therefore, once the forehead is tapped, the code is as follows:

<?php
$start_mem = memory_get_usage();
$arr = range( 1, 10000 );
foreach( $arr as $item ){
  //echo $item.',';
}
$end_mem = memory_get_usage();
echo " use mem : ". ( $end_mem - $start_mem ) .'bytes'.PHP_EOL;

A fierce operation like a tiger, run the results 1-5, you feel:

528440bytes, about 528Kb, is almost five times as much as 100Kb. damn it, I can’t live this day.

After all, you also know that the price of memory is really high recently, and the country is also calling for low carbon, energy saving and emission reduction. If you spend 5 times more memory, it means that you emit 5 times more carbon dioxide, and it means that you have to spend more money on more memory to contribute to the stick … think about it, that’s the stick.

People are forced out, so yield can come to the rescue, the code is as follows, pay attention to the operation:

<?php
$start_mem = memory_get_usage();
function yield_range( $start, $end ){
  while( $start <= $end ){
    $start++;
    yield $start;
  }
}
foreach( yield_range( 0, 9999 ) as $item ){
  echo $item.',';
}
$end_mem = memory_get_usage();
echo " use mem : ". ( $end_mem - $start_mem ) .'bytes'.PHP_EOL;

Run it, you feel it:

First of all, let’s look at the difference between the yield_range function and the ordinary function, that is, the ordinary function often uses return to return the result, and this is yield. Secondly, return can only be returned once in ordinary functions, and this yield can be returned many times.

So, let’s analyze the magic yield_range function of Yibo. What exactly does this yield keyword return? Let’s take a brief look:

<?php
function yield_range( $start, $end ){
  while( $start <= $end ){
    $start++;
    yield $start;
  }
}
$rs = yield_range( 1, 100 );
var_dump( $rs );
/*
object(Generator)#1 (0) {
}
*/

Yield returns an object called Generator, which implements the Iterator interface (as for the Iterator interface, you can search in the PHP manual). So, since the Iterator interface is implemented (and that’s why this thing can be iterated using foreach, understand? ), so you can have the following code:

<?php
function yield_range( $start, $end ){
  while( $start <= $end ){
    yield $start;
    $start++;
  }
}
$generator = yield_range( 1, 10 );
// valid() current() next() 都是Iterator接口中的方法
while( $generator->valid() ){
  echo $generator->current().PHP_EOL;
  $generator->next();
}

The operation results are as follows:

Here’s the point: this yield_range function seems to remember where it was last run and what was the result of the last run, and then it will continue to start at the place where it was last terminated immediately after the next run. This is not an ordinary PHP function can do!

We know that the operating system triggers a concept called “process context switching” when scheduling processes. For example, if the CPU is dispatched from Process A to Process B, then when it is dispatched from Process B to Process A again, where did Process A run and what are the temporary data results that need to be restored, otherwise, everything will have to start from scratch, and there will be a big problem. However, this yield keyword seems to implement this concept in the user state (non-system kernel level). Therefore, using yield to make iteration is even a really unprofitable thing. It can do too much.

Next, we need to know a method of generating an object, called send. look at the following code:

<?php
function yield_range( $start, $end ){
  while( $start <= $end ){
    $ret = yield $start;
    $start++;
    echo "yield receive : ".$ret.PHP_EOL;
  }
}
$generator = yield_range( 1, 10 );
$generator->send( $generator->current() * 10 );

The operation results are shown in the figure:

The send method can modify the return value of yield, but you can’t take it for granted, such as the following code. what do you think is the running result?

<?php
function yield_range( $start, $end ){
  while( $start <= $end ){
    $ret = yield $start;
    $start++;
    echo "yield receive : ".$ret.PHP_EOL;
  }
}
$generator = yield_range( 1, 10 );
foreach( $generator as $item ){
  $generator->send( $generator->current() * 10 );
}

Thought the operation result is similar to this:

<?php
yield receive : 10
yield receive : 20
yield receive : 30
yield receive : 40
yield receive : 50
yield receive : 60
yield receive : 70
yield receive : 80
yield receive : 90
yield receive : 100

However, materialism tells us:

The result was a slap in the face, you feel it:

What is the reason? The reason is that when you send to yield externally, it will automatically trigger next. Try it yourself.

Recently, a WeChat public number was opened, and all articles are here (the service number has been changed from cheap hands)

图片描述