generator(生成器)
说明
- 提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低
- 生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是, 生成器可以根据需要 yield 多次,以便生成需要迭代的值。
yield关键字
生成器函数的核心是yield
关键字。它最简单的调用形式看起来像一个return申明,不同之处在于普通return会返回值并终止函数的执行,而yield
会返回一个值给循环调用此生成器的代码并且只是暂停执行生成器函数。
generator对象说明
- Generator::current — 返回当前产生的值
public Generator::current ( void ) : mixed - Generator::key — 返回当前产生的键
public Generator::key ( void ) : mixed - Generator::next — 生成器继续执行
public Generator::next ( void ) : void - Generator::rewind — 重置迭代器
public Generator::rewind ( void ) : void
如果迭代已经开始了,这里会抛出一个异常。 - Generator::send — 向生成器中传入一个值
public Generator::send ( mixed $value ) : mixed - Generator::throw — 向生成器中抛入一个异常
public Generator::throw ( Exception $exception ) : void - Generator::valid — 检查迭代器是否被关闭
public Generator::valid ( void ) : bool - Generator::__wakeup — 序列化回调
public Generator::__wakeup ( void ) : void
例子
使用生成器来重新实现 range() 函数。 标准的 range() 函数需要在内存中生成一个数组包含每一个在它范围内的值,然后返回该数组, 结果就是会产生多个很大的数组。 比如,调用 range(0, 1000000) 将导致内存占用超过 100 MB。
做为一种替代方法, 我们可以实现一个 xrange() 生成器, 通过yield
关键字,只需要足够的内存来创建 Iterator 对象并在内部跟踪生成器的当前状态,这样只需要不到1K字节的内存。
function xrange($start, $limit, $step = 1) {
if ($start < $limit) {
if ($step <= 0) {
throw new LogicException('Step must be +ve');
}
for ($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
} else {
if ($step >= 0) {
throw new LogicException('Step must be -ve');
}
for ($i = $start; $i >= $limit; $i += $step) {
yield $i;
}
}
}
foreach (xrange(1, 9, 2) as $number) {
echo "$number "; // 1 3 5 7 9 占用内存很少,每次只需存储当前$number的值
}
按行读取文件内容
function getLines($file) {
$f = fopen($file, 'r');
try {
while ($line = fgets($f)) {
yield $line;
}
} finally {
fclose($f);
}
}
foreach (getLines("file.txt") as $n => $line) {
echo $line;
}
使用方式
function test() {
yield 1;
yield 2;
yield 3;
yield 4;
}
// foreach
$gen1 = test();
foreach($gen1 as $row) {
echo $row;
}
// genrator对象方法
$gen2 = test();
while($gen2->valid()) {
echo $gen->current();
$gen->next();
}
send的用法
function nums() {
for ($i = 0; $i < 5; ++$i) {
$cmd = (yield $i); // 必须加括号,否则语法错误
if($cmd === 'stop') return; // exit the function
}
}
$gen = nums();
foreach($gen as $v) {
if($v == 3) $gen->send('stop');
echo "{$v}\n";
}
//Output
0
1
2
3
网友评论