PHP项目迭代器与生成器

wen PHP项目 2

PHP 迭代器与生成器详解

迭代器 (Iterator)

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

PHP项目迭代器与生成器

内置迭代器接口

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
}

注意事项

  1. 生成器不可重置: 一旦遍历完成,需要重新创建生成器
  2. 参考传递: yield by reference 时注意作用域
  3. 异常处理: 生成器中的异常会被正常捕获
  4. 性能考虑: 对于小数据集,可能不如数组直接遍历快
  5. PHP版本: yield from 需要 PHP 7.0+

选择合适的迭代方式能显著提高代码的可读性和性能,特别是在处理大数据集时,生成器的内存优势尤为明显。

抱歉,评论功能暂时关闭!