PHP 迭代器与生成器详解
迭代器 (Iterator)
迭代器是一种设计模式,提供一种方法顺序访问聚合对象中的元素,而不暴露内部表示。

内置迭代器接口
interface Iterator extends Traversable {
public function current(); // 返回当前元素
public function key(); // 返回当前键
public function next(); // 移动到下一个元素
public function rewind(); // 重置迭代器
public function valid(); // 检查当前位置是否有效
}
自定义迭代器示例
class MyCollection implements Iterator {
private $items = [];
private $position = 0;
public function __construct(array $items) {
$this->items = $items;
}
public function current() {
return $this->items[$this->position];
}
public function key() {
return $this->position;
}
public function next() {
++$this->position;
}
public function rewind() {
$this->position = 0;
}
public function valid() {
return isset($this->items[$this->position]);
}
}
// 使用示例
$collection = new MyCollection(['a', 'b', 'c']);
foreach ($collection as $key => $value) {
echo "$key: $value\n";
}
使用 ArrayIterator
$data = ['name' => 'John', 'age' => 30, 'city' => 'Beijing'];
$iterator = new ArrayIterator($data);
foreach ($iterator as $key => $value) {
echo "$key: $value\n";
}
生成器 (Generator)
生成器是PHP 5.5引入的功能,提供了一种更简单的方式实现迭代器,无需编写完整的Iterator类。
基本语法
function getNumbers() {
for ($i = 1; $i <= 5; $i++) {
yield $i;
}
}
$generator = getNumbers();
foreach ($generator as $number) {
echo $number . "\n";
}
带键值对的生成器
function getUserData() {
yield 'id' => 1;
yield 'name' => 'Alice';
yield 'email' => 'alice@example.com';
}
foreach (getUserData() as $key => $value) {
echo "$key: $value\n";
}
引用生成器
function &getReference() {
$value = 0;
while ($value < 3) {
yield $value;
$value++;
}
}
$gen = getReference();
foreach ($gen as &$value) {
$value *= 2;
echo $value . "\n"; // 输出: 0, 2, 4
}
生成器的高级用法
从数组生成
function generateFromArray(array $items) {
foreach ($items as $key => $value) {
yield $key => $value;
}
}
$items = ['a', 'b', 'c'];
$gen = generateFromArray($items);
或者使用 yield from:
```php
function generateWithPrefix(array $items) {
yield from $items; // 直接将数组元素yield出去
}
// yield from 也可以用于委托另一个生成器
function generateEven() {
for ($i = 2; $i <= 10; $i += 2) {
yield $i;
}
}
function generateOdd() {
for ($i = 1; $i <= 9; $i += 2) {
yield $i;
}
}
function generateAllNumbers() {
yield from generateEven();
yield from generateOdd();
}
发送值到生成器
function inputReceiver() {
$output = '';
while (true) {
$input = yield $output;
$output = "Received: " . $input;
}
}
$gen = inputReceiver();
echo $gen->current() . "\n"; // 初始输出: ""
echo $gen->send('Hello') . "\n"; // 输出: Received: Hello
echo $gen->send('World') . "\n"; // 输出: Received: World
实际应用场景
处理大文件
function readLargeFile($filename) {
$handle = fopen($filename, 'r');
if ($handle === false) {
throw new Exception('Cannot open file');
}
while (($line = fgets($handle)) !== false) {
yield trim($line);
}
fclose($handle);
}
// 逐行处理1GB的大文件
foreach (readLargeFile('huge_log.txt') as $line) {
processLine($line);
}
分页数据生成
class PaginatedData {
private $totalPages;
public function __construct($totalPages) {
$this->totalPages = $totalPages;
}
public function getData($page) {
// 模拟API调用
return range(($page-1)*10 + 1, $page*10);
}
public function getAllData() {
for ($page = 1; $page <= $this->totalPages; $page++) {
$data = $this->getData($page);
foreach ($data as $item) {
yield $item;
}
}
}
}
$paginated = new PaginatedData(3);
foreach ($paginated->getAllData() as $data) {
echo $data . " "; // 1 2 3 ... 30
}
迭代器 vs 生成器 对比
| 特性 | 迭代器 | 生成器 |
|---|---|---|
| 内存使用 | 所有数据加载到内存 | 按需生成,内存友好 |
| 实现复杂度 | 需要实现多个方法 | 简单,函数+ yield |
| 可重用性 | 可以重置和多次遍历 | 不可重置,只能遍历一次 |
| 功能完整度 | 完整迭代器功能 | 有限功能(无反向迭代等) |
| 适用场景 | 小数据量、需要多次遍历 | 大数据量、单向遍历 |
| 性能 | 初始化需要内存 | 初始化快速,延迟执行 |
最佳实践
// 1. 使用生成器处理无限序列
function fibonacci() {
$a = 0;
$b = 1;
while (true) {
yield $a;
[$a, $b] = [$b, $a + $b];
}
}
// 2. 结合 array_walk 和生成器
function arrayWalkWithGenerator(array $array, callable $callback) {
foreach ($array as $key => $value) {
yield $callback($value, $key);
}
}
// 3. 延迟执行过滤
function filterGenerator(callable $callback, iterable $items) {
foreach ($items as $item) {
if ($callback($item)) {
yield $item;
}
}
}
// 使用示例
$numbers = [1, 2, 3, 4, 5, 6];
$evenNumbers = filterGenerator(function($n) {
return $n % 2 === 0;
}, $numbers);
foreach ($evenNumbers as $num) {
echo $num . " "; // 2 4 6
}
注意事项
- 生成器不可重置: 一旦遍历完成,需要重新创建生成器
- 参考传递: yield by reference 时注意作用域
- 异常处理: 生成器中的异常会被正常捕获
- 性能考虑: 对于小数据集,可能不如数组直接遍历快
- PHP版本: yield from 需要 PHP 7.0+
选择合适的迭代方式能显著提高代码的可读性和性能,特别是在处理大数据集时,生成器的内存优势尤为明显。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播