PHP 生成器与迭代器对比
PHP 中的迭代器和生成器都可以用来实现同样的功能。总体来说,迭代器 ( Iterator ) 出现的比较早,在 5.1.0 版本中就出现了,也比较完备,提供了一大堆类和接口支持。比如支持数组迭代的 ArrayIterator
、比如支持目录迭代的 DirectoryIterator
,还有支持使用正则表达式过滤其它迭代器的 RegexIterator
。
而生成器出现的比较晚,在 5.5.0 版本中才引入。并且大部分迭代器都还不支持生成器。甚至在生成器时代非常有用的 range()
函数都还不支持。
虽然生成器出现的比较晚。但它足够简单,尤其是相比较于迭代器要实现复杂的方法。生成器只要用好 yield
就可以了。
例如我们要实现一个从文件中按行读取文件。使用生成器实现一个,几乎都要实现 5 个方法
<?php class LineIterator implements Iterator { protected $fileHandle; protected $line; protected $i; public function __construct($fileName) { if (!$this->fileHandle = fopen($fileName, 'r')) { throw new RuntimeException('Couldn\'t open file "' . $fileName . '"'); } } public function rewind() { fseek($this->fileHandle, 0); $this->line = fgets($this->fileHandle); $this->i = 0; } public function valid() { return false !== $this->line; } public function current() { return $this->line; } public function key() { return $this->i; } public function next() { if (false !== $this->line) { $this->line = fgets($this->fileHandle); $this->i++; } } public function __destruct() { fclose($this->fileHandle); } }
而实现同样的功能,对于生成器来说,只需要聊聊的几行
<?php function getLinesFromFile($fileName) { if (!$fileHandle = fopen($fileName, 'r')) { throw new RuntimeException('Couldn\'t open file "' . $fileName . '"'); } while (false !== $line = fgets($fileHandle)) { yield $line; } fclose($fileHandle); }
但是,生成器并不是没有代价。生成器可以看成是一种只知道一直往前走的迭代器:生成器是只向前 ( forward-only ) 的迭代器,一旦迭代开始就无法重置。
这也意味着同一个生成器不能多次迭代:需要通过再次调用生成器函数来重建生成器。