/**
* 生成器
* 提供一种简单的方法来实现简单的迭代器,而不用为实现迭代器接口那样去定义复杂的和大开销的类
* 允许用foreach来迭代数据集,而不用在内存中建立数组,内存中建立数组会导致内存超限制,并且需要大量的处理时间来生成数组
* 切记:生成器函数的执行会延迟到迭代它的结果开始时
* 生成器不能够有返回值,这会导致一个编译错误。但空的返回语句在生成器内部是符合语法的,它会终止生成器
* 生成器只能迭代一次,不能多次迭代
*/
//eg1:
$some_state = 'initial';
function gen() {
global $some_state;
$some_state = 'changed';
}
function peek_state() {
global $some_state;
echo "\$some_state = $some_state";
}
gen();
peek_state(); //$some_state = 'changed'
//eg1-1:
$some_state = 'initial';
function gen() {
global $some_state;
$some_state = 'changed';
yield 1;
yield 2;
}
function peek_state() {
global $some_state;
echo "\$some_state = $some_state\n";
}
$result = gen();
peek_state(); //$some_state = 'initial',生成器函数的执行会延迟到迭代它的结果开始时
foreach($result as $val) {
echo "iteration: $val\n";
echo "\$some_state = $some_state\n"; //iteration: 1 $some_state = changed iteration: 2 $some_state = changed
}
//eg1-2:生成器只能迭代一次,再次迭代报错
foreach($result as $val) {
echo $val;
}
/*
* eg2:
* Note that we can change $number within the loop, and
* because the generator is yielding references, $value
* within gen_reference() changes.
*/
function &gen_reference() {
$value = 3;
while ($value > 0) {
yield $value;
}
}
foreach (gen_reference() as &$number) {
echo (--$number).'... ';
}
/**
* eg3:Fibonacci斐波纳契数列
* @param type $item
*/
function fibonacci($item) {
$a = 0;
$b = 1;
for ($i = 0; $i < $item; $i++) {
$a = $b - $a;
$b = $a + $b;
yield $a;
}
}
$fibo = fibonacci(10);
foreach ($fibo as $value) {
echo "$value\n";
}
/**
* eg4:预加载缓存机制
*/
class CachedGenerator {
protected $cache = [];
protected $generator = null;
public function __construct($generator) {
$this->generator = $generator;
}
public function generator() {
foreach($this->cache as $item) yield $item;
while( $this->generator->valid() ) {
$this->cache[] = $current = $this->generator->current();
$this->generator->next();
yield $current;
}
}
}
class Foobar {
protected $loader = null;
protected function loadItems() {
foreach(range(0,10) as $i) {
usleep(200000);
yield $i;
echo 'a';
}
}
public function getItems() {
$this->loader = $this->loader ?: new CachedGenerator($this->loadItems());
return $this->loader->generator();
}
}
$f = new Foobar;
# First
print "First\n";
foreach($f->getItems() as $i) {
print $i . "\n";
if( $i == 5 ) {
break;
}
}
# Second (items 1-5 are cached, 6-10 are loaded)
print "Second\n";
foreach($f->getItems() as $i) {
print $i . "\n";
}
# Third (all items are cached and returned instantly)
print "Third\n";
foreach($f->getItems() as $i) {
print $i . "\n";
}