2015年6月1日 星期一

PHP Generators(生成器)

PHP 5.5 之後多了生成器(The Generator class)功能,Generator 實作了  Iterator 介面,所以也是一個簡單的迭代器(iterators)。
yield 關鍵字可用來產生 Generator。

範例:
有時我們會用 range() 函式產生一個陣列,裡面元素從1~5,之後用 foreach 做其它處理。
$a = range(1,5); // $a=array(1,2,3,4,5);
foreach($a as $v){
    echo $v*$v;
}

若使用 Generator 生成器的方式,可省去產生完整陣列的步驟,也就減少了產生完整陣列所佔用的記憶體。
// 使用 yield 自訂一個有迭代器功能的生成器
function xrange($start, $end) {
    for ($i = $start; $i <= $end; $i++) {
        yield $i;
    }
}
$a = xrange(1,5);
var_dump($a); // object(Generator)#1 (0) {},$a 是 Generator 生成器物件
var_dump($a instanceof Iterator); // bool(true),$a 也實做了 Iterator 迭代器的功能
foreach($a as $v){ // $a 不需要先產生所有元素
    echo $v*$v;
}

可使用的方法:rewind()、current()、key()、next()、valid()
// 另一個跌代器,1~3
$b = xrange(1,3);
var_dump($b->valid()); // true
var_dump($b->key()); // 0
var_dump($b->current()); // 1

$b->next(); // 下一個
$b->next(); // 到最後一個
var_dump($b->valid()); // true
var_dump($b->key()); // 2
var_dump($b->current()); // 3

$b->next(); // 再下一個,沒元素了
var_dump($b->valid()); // false
var_dump($b->key()); // null
var_dump($b->current()); // null
若在過程中使用 rewind(),會出現「Uncaught exception 'Exception' with message 'Cannot rewind a generator that was already run」錯誤,因為生成器開始使用就不能用rewind,生成器rewind說明:Generator::rewind(If iteration has already begun, this will throw an exception).

傳送資料給 Generator:send()
function printer() {
    while (true) {
        $string = yield;
        echo $string;
    }
}

$printer = printer();
$printer->send('Hello world!'); // Hello world!


參考:
在PHP中使用协程实现多任务调度
PHP5.5或将引入Generators
PHP: Generators overview - Manual
PHP: Generator syntax - Manual
PHP: Generator::send - Manual
重温PHP手册 – 生成器

沒有留言:

張貼留言