php之Generator生成器及yield

96
依恋灬
2017.05.22 10:57* 字数 561

生成器的核心是一个yield关键字,一个生成器函数看起来像一个普通的函数,不同的是:普通函数返回一个值,而一个生成器可以yield生成许多它所需要的值。生成器函数被调用时,返回的是一个可以被遍历的对象。

yield

yield和return有点类似,不过不同的是,return会返回值并且终止代码的执行,而yield会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。
实例分析
<?php
  function gen_one_to_three() {
    for ($i = 1; $i <= 3; $i++) {
        //注意变量$i的值在不同的yield之间是保持传递的。
        yield $i;
    }
}
$generator = gen_one_to_three();
var_dump($generator);
echo "<br/>";
var_dump($generator instanceof Iterator); // bool(true)
echo "<br/>";
foreach ($generator as $value) {
    echo "$value\n";
}
?>

输出如下:

Paste_Image.png

调用gen_one_to_three()的时候,里面的代码并没有真正的执行,而是返回了一个生成器对象$generator = Generator Object( ),$generator instanceof Iterator说明Generator实现了Iterator接口,可以用foreach进行遍历,每次遍历都会隐式调用current()、next()、key()、valid()等方法。(Generator类中的方法)

指定键名来生成值
$input = <<<'EOF'
1;PHP;Likes dollar signs
2;Python;Likes whitespace
3;Ruby;Likes blocks
EOF;

function input_parser($input) {
    foreach (explode("\n", $input) as $line) {
        $fields = explode(';', $line);
        $id = array_shift($fields);

        yield $id => $fields;
    }
}

foreach (input_parser($input) as $id => $fields) {
    echo "$id:\n";
    echo "    $fields[0]\n";
    echo "    $fields[1]\n";
}
?>

输出如下

Paste_Image.png
生成NULL
<?php
function gen_three_nulls() {
    foreach (range(1, 3) as $i) {
        yield;
    }
}

var_dump(iterator_to_array(gen_three_nulls()));
?>

输出如下


Paste_Image.png
使用引用来生成值
<?php
function &gen_reference() {
    $value = 3;

    while ($value > 0) {
        yield $value;
    }
}

/* 
 * 我们可以在循环中修改$number的值,而生成器是使用的引用值来生成,所以gen_reference()内部的$value值也会跟着变化。
 */
foreach (gen_reference() as &$number) {
    echo (--$number).'... ';
}
?>

输出如下

Paste_Image.png
PHP7开始允许从其他的generator,Traversable对象, 或者数组通过yield from 生成数函数 来yield值
实例分析—yield from 生成函数
<?php
function count_to_ten() {
    yield 1;
    yield 2;
    yield from [3, 4];
    yield from new ArrayIterator([5, 6]);
    yield from seven_eight();
    yield 9;
    yield 10;
}

function seven_eight() {
    yield 7;
    yield from eight();
}

function eight() {
    yield 8;
}

foreach (count_to_ten() as $num) {
    echo "$num ";
}
?>

输出如下:

Paste_Image.png
实例分析—yield from and return values
function count_to_ten() {
    yield 1;
    yield 2;
    yield from [3, 4];
    yield from new ArrayIterator([5, 6]);
    yield from seven_eight();
    return yield from nine_ten();
}

function seven_eight() {
    yield 7;
    yield from eight();
}

function eight() {
    yield 8;
}

function nine_ten() {
    yield 9;
    return 10;
}

$gen = count_to_ten();
foreach ($gen as $num) {
    echo "$num ";
}
echo $gen->getReturn();

输出如下

Paste_Image.png

Generator

它有如下方法

<?php

Generator implements Iterator {
    
    public mixed current ( void )//返回当前产生的值
   
    public mixed key ( void ) //返回当前产生的键
    
    public void next ( void )//生成器继续执行
   
    public void rewind ( void ) //重置迭代器,如果迭代已经开始了,这里会抛出一个异常。
   
    public mixed send ( mixed $value ) //向生成器中传入一个值,当前yield接收值,然后继续执行下一个yield
  
    public void throw ( Exception $exception )  //向生成器中抛入一个异常
  
    public bool valid ( void )  //检查迭代器是否被关闭,已被关闭返回 FALSE,否则返回 TRUE
   
    public void __wakeup ( void ) //序列化回调
    
    public mixed getReturn ( void )//返回generator函数的返回值,PHP version 7+
}
?>

来看一个实例:

function test()
{
    $a = (yield 111);
    var_dump('test()->$a:'.$a);
    $b = (yield 222);
    var_dump('test()->$b:'.$b);
}
$gen = test();
echo "first:";
var_dump($gen->current());echo "<br/>";
echo "second:";
var_dump($gen->send(333));echo "<br/>";
echo "third:";
var_dump($gen->next());

输出如下

Paste_Image.png

分析:
1、第一次test函数,执行到yield,就中断了执行,这次yield停留在了yield 111
2、通过$gen->current()把111进行返回,故输出了111
3、$gen->send(333),把333传递给了test函数中的yield,被yield接收到,故 $a = 333 ,输出了 $test->$a:333,这时又重新唤醒了yield的执行,
4、$b = yield 222,执行到这时,把结果又进行了一次返回,并且进行了中断执行,这时 $gen-send(333) 等于 222,故输出了222
5、$gen->next() 又重新唤醒了 $gen的迭代执行,但不是使用send进行传递,故这时 $b没接收到任何值,所以输出了NULL

Php
Web note ad 1