Implementation of the "Go" PHP Generator (generator) and the co-process

Source: Internet
Author: User
Tags rewind scalar

Original address: https://phphub.org/topics/1430

1. Everything starts with Iterator and Generator

To make it easier for new start-up developers to understand, half of this article is about iterator interfaces (Iterator) and Generator classes, which are understood to be skipped directly.

It is necessary to know iterations and iterators before you understand most of the concepts in this article. In fact, the iteration knows what it is, but I don't know (really, there is no systematic understanding of this concept before). An iteration is a process that executes repeatedly, each time called an iteration. In fact, we often do this kind of thing, for example:

 <? php   $mapping  = [ ' red ' = ' #FF0000 ', ' GRE En ' and ' #00FF00 ', ' Blue ' + ' #0000FF '  ';  foreach  ( $mapping  as   $key  = =  $value   printf  ("Key:%d-value:%s\n",  $key ,  $value   

We can see foreach the output of its contents by iterating through the array and iterating over it. In this link, we need to focus on the array. Although our iterative process is a block of code in a foreach statement, the fact that the array has $mapping changed in each iteration means that there is also an iteration within the group. If we think of an array as an object, foreach actually invokes a method of that object at each iteration, letting the array make one change (iteration) within itself, and then fetching the key and value of the current array object by another method. Such an object that can traverse its internal data externally is one 迭代器对象 , and the uniform Access interface It follows is 迭代器接口(Iterator) .

PHP provides a unified iterator interface. The official PHP documentation for Iterators is described in more detail and is recommended to understand.

InterfaceIteratorextendstraversable{/** * Gets the data of the element that the current internal scalar points to*/     Public Mixed  Current(void)/** * Get current scalar*/     PublicScalarKey(void)/** * Move to the next scalar*/     PublicvoidNext(void)/** * Reset scalar*/     PublicvoidRewind(void)/** * Check whether the current scalar is valid*/     Public Booleanvalid (void)}

Let's give an example to implement a simple iterator:

classXrangeImplementsiterator{protected $start; protected $limit; protected $step; protected $i;  Public function__construct ($start,$limit,$step= 0)    {        $this->start =$start; $this->limit =$limit; $this->step =$step; }     Public function Rewind()    {        $this->i =$this-start; }     Public function Next()    {        $this->i + =$this-step; }     Public function  Current()    {        return $this-i; }     Public function Key()    {        return $this->i + 1; }     Public functionvalid () {return $this->i <=$this-limit; }}

Look at the effect of this iterator through the foreach history:

foreach (newas$key$value) {    printf$key$value );}

Output:

1 03 25 47 69 811 10

At this point we see an implementation of an iterator. Some people are excited to know about this feature and apply it to real projects, but some wonder what the eggs are for. An iterator simply turns a normal object into an object that can be traversed, which in some cases, such as an object studentscontact, is used to process student contact, enroll students through the Addstudent method, and through Getallstudent Get an array of student contact information for all registrations. We used to iterate through studentscontact::getallstudent () to get an array and then iterate over the array, but now with the iterator, as long as the class inherits this interface, it can traverse the object to get the student array, You can also work with the output data in the inside of the class before getting it.

Of course it's much more useful than that, but not too tangled here. There is something more powerful on this basis, generator.

2, generator, Generator

Although iterators only need to inherit the interface can be implemented, but it is still cumbersome, we need to define a class and implement all methods of the interface, this is very cumbersome. In some situations we need a more concise approach. The generator provides an easier way to implement a simple object iteration, comparing the way that the definition class implements the Iterator interface, with significantly reduced performance overhead and complexity.

The official PHP document says this:

The generator allows you to write code in a foreach code block to iterate over a set of data without having to create an array in memory, which will limit your memory to a maximum or take up considerable processing time. Instead, you can write a generator function, just like a normal custom function, and the normal function returns only once differently, and the generator can yield multiple times as needed to generate values that need to be iterated.

A simple example is to use the generator to re-implement the range () function. The standard range () function needs to generate an array in memory that contains every value within its scope, and then returns the array, resulting in multiple large arrays. For example, calling range (0, 1000000) will result in memory consumption exceeding MB.

As an alternative, we can implement a xrange () generator that requires only enough memory to create the Iterator object and keep track of the generator's current state internally, which requires less than 1K bytes of memory.

The official documentation gives the example above, which we have simplified here:

functionXrange$start,$limit,$step= 1) {     for($i=$start;$i<=$limit;$i+=$step) {yield$i+ 1 =$i;//The keyword yield indicates that this is a generator    }}//We can call thisforeach(xrange (0, 10, 2) as $key=$value) {    printf("%d%d\n",$key,$value);}

Perhaps you have already discovered that the output of this example is the same as the one we had earlier in the case of iterators. Actually 生成器 generated is exactly one 迭代器对象实例 , the iterator object inherits the Iterator interface, but also contains its 生成器对象 own interface, in particular, can refer to Generator the definition of the class.

When a generator is called, it returns an object that can be traversed. When you traverse this object (for example, through a foreach loop), PHP will call the generator function every time a value is needed and save the generator state after a value is generated. This allows it to revert to the calling state when it needs to produce the next value.

Once you no longer need to generate more values, the generator function can simply exit, and the code that calls the generator can continue to execute as if an array has been traversed.

The key to our attention is that yield this is the key to the generator. As we can see from the above example, the yield current value is passed to foreach, in other words, each iteration of foreach takes a value from yield, and the traversal ends when the entire traversal process no longer has yield.

We can also find that yield and return return values, but the difference is that a return returns a given result, and once returned, no new results are returned, and yield is continuously produced until it fails to produce.

In fact, the function return value of yield returns an Generator object (this object cannot be instantiated manually via new), and the object implements the Iterator interface. So Generator what is unique about yourself? Keep looking:

3. Yield

Literally, yield represents a way of giving way. This makes it possible to achieve through yield 协程 .

The core of the generator function is the yield keyword. Its simplest invocation form looks like a return declaration, except that the normal return returns a value and terminates the execution of the function, and yield returns a value to the code that loops through this generator and simply pauses the execution of the generator function.

The difference between yield and return, which is to pause the execution of the current procedure and return a value, which interrupts the current procedure and returns a value. Pausing the current process means that the transfer of processing rights continues from the previous level until the paused process is called again at the previous level, and the process resumes from the last paused position. What is it like? If the reader before reading the article has been in the bird Brother's article in a cursory glance, you should know that this is very much like an operating system process scheduling management, multiple processes in a CPU core execution, under the system scheduling each process to execute a piece of instruction is suspended, switch to the next process, This looks like it is performing multiple tasks at the same time.

But that's just not enough, yield's more important feature is the ability to receive a value in addition to returning a value!

function printer () {    while (true) {        printf("Receive:%s\n" , yield);    } }$printer = printer (); $printer->send (' hello '); $printer->send (' World ');

The above example outputs the following:

Receive: helloreceive: World

Refer to PHP official English document: Generator object We can tell that the Generator object, in addition to the necessary methods in implementing the Iterator interface, has a send method that passes a value to the yield statement and continues execution from the yied statement until Once again encountering yield after control returns to the outside.

We've also learned a problem before, that yield can break in its place and return a value, so can we do it at the same time 接收 返回 ? Of course, this is the root of the implementation process. We make changes to the above code:

<?PHPfunctionprinter () {$i= 0;  while(true) {        printf("Receive:%s\n", (yield + +)$i)); }}$printer=printer ();printf("%d\n",$printer- Current());$printer->send (' Hello ');printf("%d\n",$printer- Current());$printer->send (' World ');printf("%d\n",$printer- Current());

The output reads as follows:

1receive: Hello2receive: World 3

currentThe method is the necessary method for the iterator ( Iterator ) interface, where each iteration of the foreach statement obtains the current value and then invokes the iterator's next method. We call the current method to get the value manually in order for the program not to execute indefinitely.

The above example is enough to indicate that the yield is in that position as two-way transmission 工具 , and the conditions for the implementation of the co-process are already available.

4. Co-process  

This Part I do not intend to lengthy, the beginning of this article has given the Bird Brother blog More perfect article, the purpose of this article is to supplement the details of the Generator.

We need to know that for a single-core processor, multitasking is done by having each task perform for a period of time, then interrupting, letting another task execute and then executing the next after the break, so again and again. Because its execution is fast, it makes the external view that multiple tasks are actually "parallel".

Brother Bird's article says:

The term "collaboration" in multi-tasking collaboration is a good illustration of how to do this: it requires that the currently running task automatically pass control back to the scheduler so that other tasks can be run. This is the opposite of preemption multitasking: The scheduler can interrupt tasks that have been running for a while, whether they like it or not. Collaboration multitasking is used in earlier versions of Windows (Windows95) and Mac OS, but they later switch to using preemptive multitasking. The reason is quite clear: if you rely on the program to automatically surrender control, then some malicious program will easily occupy the entire CPU, not to share with other tasks.

By combining the previous examples, we can see that yield acts as a way to interrupt a task itself and then go back to the outside to continue execution. This feature can be used to realize the function of multi-task scheduling, with the two-way communication function of yield, in order to realize the communication between task and dispatcher.

This is especially important when reading and manipulating stream resources, and we can greatly improve the processing power of the program for concurrent streaming resources, such as implementing TCP server. The above is a more detailed example of the use of the coprocessor in PHP for multi-tasking scheduling. This article does not repeat.

5. Summary

PHP from 5.4 to today's more stable PHP 7, you can see a lot of new features to make the language more powerful and perfect, gradually from the pure Web language has become more widely applicable surface, as a phper indeed should not stop, we still have a lot of things need to continue to learn and strengthen.

Although the phrase "PHP is the best language in the world" is a joke, it's undeniable that PHP is trying to get better, even if it's not the best, right?

Implementation of the "Go" PHP Generator (generator) and the co-process

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.