PHP学习笔记17:迭代器和生成器

本文介绍了PHP中的迭代器和生成器。迭代器允许自定义遍历集合的方式,通过实现Iterator接口,包括current、next、rewind、key和valid等方法。生成器则提供了一种简洁的创建迭代器的方式,使用yield关键字,可以在遍历大型数据集时避免内存占用过多。文中还展示了如何使用生成器进行字符串的单词和字母遍历,并讨论了生成器的优点及其在处理超大序列时的效率优势。
摘要由CSDN通过智能技术生成

PHP学习笔记17:迭代器和生成器

image-20211129162010327

图源:php.net

迭代器相关概念广泛存在于各种编程语言和设计模式中,这里推荐两篇我的相关文章:

迭代器

php中,可以通过实现Iterator接口的方式实现一个迭代器:

<?php
class MyClass implements Iterator
{
    public function current(): mixed
    {
    }
    public function next(): void
    {
    }
    public function rewind(): void
    {
    }
    public function key(): mixed
    {
    }
    public function valid(): bool
    {
    }
}

这些方法的作用是:

  • current,返回游标对应的当前元素。
  • next,移动游标到下一个元素。
  • rewind,重置游标到开始位置。
  • key,返回游标位置。
  • valid,游标当前的位置是否有效(用于判断是否结束遍历)。

php的迭代器接口比Python中的更复杂,但好处是包含rewind方法,可以重置迭代器以重复迭代。

下面用一个可以将字符串内单词分词的程序作为示例:

<?php
class Sentence implements Iterator
{
    private array $words;
    private int $index = 0;
    public function __construct(string $string)
    {
        $this->words = str_word_count($string, 1);
    }
    public function current(): mixed
    {
        return $this->words[$this->index];
    }
    public function rewind(): void
    {
        $this->index = 0;
    }
    public function next(): void
    {
        $this->index++;
    }
    public function key(): mixed
    {
        return $this->index;
    }
    public function valid(): bool
    {
        return isset($this->words[$this->index]);
    }
}
$sentence = new Sentence("hello world, how are you!");
foreach ($sentence as $word) {
    echo $word . PHP_EOL;
}
echo PHP_EOL;
// hello
// world
// how
// are
// you

php预设了一些常用的迭代器,具体请参考官方手册迭代器

生成器

生成器可以看做是一种特殊的迭代器,可以使用生成器函数来便捷地创建一个生成器:

<?php
function sentence(string $str)
{
    $words = str_word_count($str, 1);
    foreach ($words as $key => $val) {
        yield $val;
    }
}
foreach (sentence("hello world, how are you!") as $word) {
    echo $word . PHP_EOL;
}
// hello
// world
// how
// are
// you

生成器的优点在于,相比较迭代器,它的实现代码更少,且还可以用yield from语句将调用委托给另一个生成器,实现类似的生成器多级套用的方式。这点在Python的async包实现并发时相当常见。

这里展示sentence套用一个将字符串分解为字母遍历的生成器:

<?php
function char(string $str): Generator
{
    $len = strlen($str);
    if ($len == 0) {
        yield "";
        return;
    }
    $index = 0;
    do {
        yield substr($str, $index, 1);
        $index++;
    } while ($index <= $len - 1);
}
function sentence(string $str): Generator
{
    $words = str_word_count($str, 1);
    foreach ($words as $key => $val) {
        yield from char($val);
    }
}
foreach (sentence("hello world, how are you!") as $word) {
    echo $word . " ";
}
echo PHP_EOL;
// h e l l o w o r l d h o w a r e y o u 

通过使用生成器,我们可以避免程序中因为遍历大型数组导致的内存占用过多的情况:

<?php
function xrange(int $start, int $end, int $step): Generator
{
    if ($step <= 0) {
        throw new Exception("invlid step param.");
    }
    if ($start < $end) {
        for ($i = $start; $i <= $end; $i += $step) {
            yield $i;
        }
    } else {
        for ($i = $start; $i >= $end; $i -= $step) {
            yield $i;
        }
    }
}
function print_generator(Generator $gen)
{
    foreach ($gen as $val) {
        echo $val . " ";
    }
    echo PHP_EOL;
}
$gen1 = xrange(1, 10, 1);
$gen2 = xrange(20, 3, 3);
print_generator($gen1);
print_generator($gen2);
// 1 2 3 4 5 6 7 8 9 10 
// 20 17 14 11 8 5 

相比较内置的range函数,这里的xrange函数可以用于生成超大长度的序列,且只会占用很小的内存。

需要注意的是,生成器函数本身返回的是生成器,其实质上充当了生成器工厂方法的作用。yield产出的数据只不过是生成器遍历时每次返回的迭代结果,而不是生成器函数的返回值。这点新手很容易搞混淆。

生成器是内置类型Generator的实例,所以为了明确起见,可以将生成器函数的返回值标注为Generator类型。

因为在学习Python的过程中详细总结了生成器的相关内容,所以这里只简单介绍了php中生成器和迭代器的用法,更多生成器的内容可以阅读Python学习笔记16:生成器

谢谢阅读。

往期内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值