ホームページ  >  記事  >  バックエンド開発  >  PHP ジェネレーターの使用方法を詳しく説明します。

PHP ジェネレーターの使用方法を詳しく説明します。

藏色散人
藏色散人転載
2020-12-01 13:50:104139ブラウズ

PHP ジェネレーターの使用方法を学ぶ

ジェネレーターとは何ですか?

派手な名前を聞くと、何かを作成するための関数のように感じますが、実際には、ジェネレーターは反復のためのイテレーターです。これにより、単純なオブジェクトの反復を実装する簡単な方法が提供され、クラスを定義して Iterator インターフェイスを実装する場合と比較して、パフォーマンスのオーバーヘッドと複雑さが大幅に軽減されます。

お勧め: 「PHP ビデオ チュートリアル

長い間話した後は、コードを直接見たほうが直感的です。

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 を戻り値と見なすことができます。最後に、外側のループが使用される場合、一度に 1 つの yield の戻り値が取得されます。この例では、ループが 3 回行われ、3 つの数値 1、2、および 3 が返されます。次に、ループの外側にさらに 2 行の yield を書き込み、それぞれ 1000 と 1001 を出力します。したがって、外側の foreach ループは合計 5 回出力します。

これは驚くべきことです。これは明らかにメソッドですが、なぜループできるのに、ループ本体を返すための非常に奇妙な形式です。この 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

上記のコードは、1,000,000 回のループ後に結果を取得するだけですが、直感的に確認することもできます。ジェネレーターを使用したバージョンは 2M のメモリを消費するだけですが、ジェネレーターを使用しないバージョンは 35M のメモリを消費します。これは 10 倍以上の差であり、差が大きいほど、違いはより顕著になります。したがって、一部の専門家は、ジェネレーターは PHP で最も過小評価されている機能であると述べています。

ジェネレーターの応用

次に、ジェネレーターの基本的な応用方法をいくつか見てみましょう。

null 値を返して中断

もちろん、ジェネレーターは単に降伏するだけで null 値を返すこともでき、値がなければ null 値を返すこともできます。 return; をメソッド内で直接使用すると、ジェネレーターの継続的な実行を中断することもできます。次のコードでは、$i = 4 の場合に null 値を返します。つまり、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 &#39;key.&#39; . $i => $i + 1;
    }
}
foreach (test5() as $k=>$t) {
    echo $k . &#39;:&#39; . $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 メソッドを通じてジェネレーターに値を渡すことができます。渡された値は、ジェネレーターの現在の利回りの戻り値として扱われます。次に、この値に基づいて、外部条件に基づいてジェネレーターの実行を中断するなどの判断を下すことができます。

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

上記のコードは理解しにくいかもしれませんが、コメントの行を覚えておいてください(通常はループ値を取得します。値が外部から送信された場合、yield が取得するのは外部からの値です) 。さらに、yield の値を取得するには、変数を括弧で囲む必要があります。

yield from syntax

yield from syntax

yield from syntax は、実際には、別の反復可能なオブジェクトからデータを 1 つずつ取得し、ジェネレーターの戻り値を形成することを指します。コードを見てください。

function test7()
{
    yield from [1, 2, 3, 4];
    yield from new ArrayIterator([5, 6]);
    yield from test1();
}
foreach (test7() as $t) {
    echo &#39;test7:&#39;, $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 を使用して数量を取得できません。

$c = count(test1()); // Warning: count(): Parameter must be an array or an object that implements Countable
// echo $c, PHP_EOL;
count を使用してジェネレーターの数を取得すると、Warning 警告が直接報告されます。 count の特性により、直接出力では常に 1 が表示されます (強制的に配列に入力すると 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 して 1 つずつ返すだけで済みます。実際のプロジェクトで試してみる価値は十分にありますが、試した後は必ず友達と共有することを忘れないでください。ほとんどの人はこの機能に慣れていない可能性があります。 !

テスト コード: https://github.com/zhangyue0503/dev-blog/blob/master/php/202002/source/PHP ジェネレーターの使用法を学ぶ。php

参考ドキュメント: https://www.php.net/manual/zh/ language.generators.overview.php https://www.php.net/manual/zh/class.generator.php

以上がPHP ジェネレーターの使用方法を詳しく説明します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はzhihu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。