音效素材网提供各类素材,打造精品素材网站!

站内导航 站长工具 投稿中心 手机访问

音效素材

PHP 生成器的使用详解
日期:2021-09-06 21:13:29   来源:脚本之家

什么是生成器?

听着高大上的名字,感觉像是创造什么东西的一个功能,实际上,生成器是一个用于迭代的迭代器。它提供了一种更容易的方式来实现简单的对象迭代,相比较定义类实现Iterator接口的方式,性能开销和复杂性大大降低。

说了半天不如直接看看代码更直观。

function test1()
{
    for ($i = 0; $i < 3; $i++) {
        yield $i + 1;
    }
    yield 1000;
    yield 1001;
}

foreach (test1() as $t) {
    echo $t, PHP_EOL;
}

// 1
// 2
// 3
// 1000
// 1001

就是这么简单的一段代码。首先,生成器必须在方法中并使用 yield 关键字;其次,每一个 yield 可以看作是一次 return ;最后,外部循环时,一次循环取一个 yield 的返回值。在这个例子,循环三次返回了1、2、3这三个数字。然后在循环外部又写了两行 yield 分别输出了1000和1001。因此,外部的 foreach 一共循环输出了五次。

很神奇吧,明明是一个方法,为什么能够循环它而且还是很奇怪的一种返回循环体的格式。我们直接打印这个 test() 方法看看打印的是什么:

// 是一个生成器对象
var_dump(test1());

// Generator Object
// (
// )

当使用了 yield 进行内容返回后,返回的是一个 Generator 对象。这个对象就叫作生成器对象,它不能直接被 new 实例化,只能通过生成器函数这种方式返回。这个类包含 current() 、 key() 等方法,而且最主要的这个类实现了 Iterator 接口,所以,它就是一个特殊的迭代器类。

Generator implements Iterator {
    /* 方法 */
    public current ( void ) : mixed
    public key ( void ) : mixed
    public next ( void ) : void
    public rewind ( void ) : void
    public send ( mixed $value ) : mixed
    public throw ( Exception $exception ) : void
    public valid ( void ) : bool
    public __wakeup ( void ) : void
}

生成器有什么用?

搞了半天不就是个迭代器嘛?搞这么麻烦干嘛,直接用迭代器或者在方法中直接返回一个数组不就好了吗?没错,正常情况下真的没有这么麻烦,但是如果是在数据量特别大的情况下,这个生成器就能发挥它的强大威力了。生成器最最强大的部分就在于,它不需要一个数组或者任何的数据结构来保存这一系列数据。每次迭代都是代码执行到 yield 时动态返回的。因此,生成器能够极大的节约内存。

// 内存占用测试
$start_time = microtime(true);
function test2($clear = false)
{
    $arr = [];
    if($clear){
        $arr = null;
        return;
    }
    for ($i = 0; $i < 1000000; $i++) {
        $arr[] = $i + 1;
    }
    return $arr;
}
$array = test2();
foreach ($array as $val) {
}
$end_time = microtime(true);

echo "time: ", bcsub($end_time, $start_time, 4), PHP_EOL;
echo "memory (byte): ", memory_get_usage(true), PHP_EOL;

// time: 0.0513
// memory (byte): 35655680

$start_time = microtime(true);
function test3()
{
    for ($i = 0; $i < 1000000; $i++) {
        yield $i + 1;
    }
}
$array = test3();
foreach ($array as $val) {

}
$end_time = microtime(true);

echo "time: ", bcsub($end_time, $start_time, 4), PHP_EOL;
echo "memory (byte): ", memory_get_usage(true), PHP_EOL;

// time: 0.0517
// memory (byte): 2097152

上述代码只是简单的进行 1000000 个循环后获取结果,不过也可以直观地看出。使用生成器的版本仅仅消耗了 2M 的内存,而未使用生成器的版本则消耗了 35M 的内存,直接已经10多倍的差距了,而且越大的量差距超明显。因此,有大神将生成器说成是PHP中最被低估了的一个特性。

生成器的应用

接下来我们来看看生成器的一些基本的应用方式。

返回空值以及中断

生成器当然也可以返回空值,直接 yield; 不带任何值就可以返回一个空值了。而在方法中直接使用 return; 也可以用来中断生成器的继续执行。下面的代码我们在 \i = 4; 的时候返回的是个空值,也就是不会输出 5 (因为我们返回的是i=4;的时候返回的是个空值,也就是不会输出5(因为我们返回的是i + 1 )。然后在 $i == 7 的时候使用 return; 中断生成器的继续执行,也就是循环最多只会输出到 7 就结束了。

// 返回空值以及中断
function test4()
{
    for ($i = 0; $i < 10; $i++) {
        if ($i == 4) {
            yield; // 返回null值
        }
        if ($i == 7) {
            return; // 中断生成器执行
        }
        yield $i + 1;
    }
}

foreach (test4() as $t) {
    echo $t, PHP_EOL;
}


// 1
// 2
// 3
// 4

// 5
// 6
// 7

返回键值对形式

不要惊讶,生成器真的是可以返回键值对形式的可遍历对象供 foreach 使用的,而且语法非常好记: yield key => value; 是不是和数组项的定义形式一模一样,非常直观好理解。

function test5()
{
    for ($i = 0; $i < 10; $i++) {
        yield 'key.' . $i => $i + 1;
    }
}

foreach (test5() as $k=>$t) {
    echo $k . ':' . $t, PHP_EOL;
}

// key.0:1
// key.1:2
// key.2:3
// key.3:4
// key.4:5
// key.5:6
// key.6:7
// key.7:8
// key.8:9
// key.9:10

外部传递数据

我们可以通过 Generator::send 方法来向生成器中传入一个值。传入的这个值将会被当做生成器当前 yield 的返回值。然后我们根据这个值可以做一些判断,比如根据外部条件中断生成器的执行。

function test6()
{
    for ($i = 0; $i < 10; $i++) {
        // 正常获取循环值,当外部send过来值后,yield获取到的就是外部传来的值了
        $data = (yield $i + 1);
        if($data == 'stop'){
            return;
        }
    }
}
$t6 = test6();
foreach($t6 as $t){
    if($t == 3){
        $t6->send('stop');
    }
    echo $t, PHP_EOL;
}

// 1
// 2
// 3

上述代码理解起来可能比较绕,但是注意记住注释的那行话就行了(正常获取循环值,当外部send过来值后,yield获取到的就是外部传来的值了)。另外,变量获取 yield 的值,必须要用括号括起来。

yield from 语法

yield from 语法其实就是指的从另一个可迭代对象中一个一个的获取数据并形成生成器返回。直接看代码

function test7()
{
    yield from [1, 2, 3, 4];
    yield from new ArrayIterator([5, 6]);
    yield from test1();
}
foreach (test7() as $t) {
    echo 'test7:', $t, PHP_EOL;
}

// test7:1
// test7:2
// test7:3
// test7:4
// test7:5
// test7:6
// test7:1
// test7:2
// test7:3
// test7:1000

在 test7() 方法中,我们使用 yield from 分别从普通数组、迭代器对象、另一个生成器中获取数据并做为当前生成器的内容进行返回。

小惊喜

生成器可以用count获取数量吗?

抱歉,生成器是不能用count来获取它的数量的。

$c = count(test1()); // Warning: count(): Parameter must be an array or an object that implements Countable
// echo $c, PHP_EOL;

使用 count 来获取生成器的数量将直接报 Warning 警告。直接输出将会一直显示是 1 ,因为 count 的特性(强制转换成数组都会显示 1 )。

使用生产器来获取斐波那契数列

// 利用生成器生成斐波那契数列
function fibonacci($item)
{
    $a = 0;
    $b = 1;
    for ($i = 0; $i < $item; $i++) {
        yield $a;
        $a = $b - $a;
        $b = $a + $b;
    }
}

$fibo = fibonacci(10);
foreach ($fibo as $value) {
    echo "$value\n";
}

这段代码就不多解释了,非常直观的一段代码了。

总结

生成器绝对是PHP中的一个隐藏的宝藏,不仅是对于内存节约来说,而且语法其实也非常的简洁明了。我们不需要在方法内部再多定义一个数组去存储返回值,直接 yield 一项一项的返回就可以了。在实际的项目中完全值得尝试一把,但是尝试完了别忘了和小伙伴们分享,大部分人可能真的没有接触过这个特性哦!!

测试代码: github.com/zhangyue050…

以上就是PHP 生成器的使用详解的详细内容,更多关于PHP 生成器的资料请关注其它相关文章!

    您感兴趣的教程

    在docker中安装mysql详解

    本篇文章主要介绍了在docker中安装mysql详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编...

    详解 安装 docker mysql

    win10中文输入法仅在桌面显示怎么办?

    win10中文输入法仅在桌面显示怎么办?

    win10系统使用搜狗,QQ输入法只有在显示桌面的时候才出来,在使用其他程序输入框里面却只能输入字母数字,win10中...

    win10 中文输入法

    一分钟掌握linux系统目录结构

    这篇文章主要介绍了linux系统目录结构,通过结构图和多张表格了解linux系统目录结构,感兴趣的小伙伴们可以参考一...

    结构 目录 系统 linux

    PHP程序员玩转Linux系列 Linux和Windows安装

    这篇文章主要为大家详细介绍了PHP程序员玩转Linux系列文章,Linux和Windows安装nginx教程,具有一定的参考价值,感兴趣...

    玩转 程序员 安装 系列 PHP

    win10怎么安装杜比音效Doby V4.1 win10安装杜

    第四代杜比®家庭影院®技术包含了一整套协同工作的技术,让PC 发出清晰的环绕声同时第四代杜比家庭影院技术...

    win10杜比音效

    纯CSS实现iOS风格打开关闭选择框功能

    这篇文章主要介绍了纯CSS实现iOS风格打开关闭选择框,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作...

    css ios c

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的办法

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的

    Win7给电脑C盘扩容的办法大家知道吗?当系统分区C盘空间不足时,就需要给它扩容了,如果不管,C盘没有足够的空间...

    Win7 C盘 扩容

    百度推广竞品词的投放策略

    SEM是基于关键词搜索的营销活动。作为推广人员,我们所做的工作,就是打理成千上万的关键词,关注它们的质量度...

    百度推广 竞品词

    Visual Studio Code(vscode) git的使用教程

    这篇文章主要介绍了详解Visual Studio Code(vscode) git的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...

    教程 Studio Visual Code git

    七牛云储存创始人分享七牛的创立故事与

    这篇文章主要介绍了七牛云储存创始人分享七牛的创立故事与对Go语言的应用,七牛选用Go语言这门新兴的编程语言进行...

    七牛 Go语言

    Win10预览版Mobile 10547即将发布 9月19日上午

    微软副总裁Gabriel Aul的Twitter透露了 Win10 Mobile预览版10536即将发布,他表示该版本已进入内部慢速版阶段,发布时间目...

    Win10 预览版

    HTML标签meta总结,HTML5 head meta 属性整理

    移动前端开发中添加一些webkit专属的HTML5头部标签,帮助浏览器更好解析HTML代码,更好地将移动web前端页面表现出来...

    移动端html5模拟长按事件的实现方法

    这篇文章主要介绍了移动端html5模拟长按事件的实现方法的相关资料,小编觉得挺不错的,现在分享给大家,也给大家...

    移动端 html5 长按

    HTML常用meta大全(推荐)

    这篇文章主要介绍了HTML常用meta大全(推荐),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参...

    cdr怎么把图片转换成位图? cdr图片转换为位图的教程

    cdr怎么把图片转换成位图? cdr图片转换为

    cdr怎么把图片转换成位图?cdr中插入的图片想要转换成位图,该怎么转换呢?下面我们就来看看cdr图片转换为位图的...

    cdr 图片 位图

    win10系统怎么录屏?win10系统自带录屏详细教程

    win10系统怎么录屏?win10系统自带录屏详细

    当我们是使用win10系统的时候,想要录制电脑上的画面,这时候有人会想到下个第三方软件,其实可以用电脑上的自带...

    win10 系统自带录屏 详细教程

    + 更多教程 +
    ASP编程JSP编程PHP编程.NET编程python编程