web-dev-qa-db-ja.com

ジェネレーターとアレイの違いは何ですか?

本日、PHPチームは PHP 5.5.0 バージョンをリリースしました。 generators。Reading ドキュメント 、I配列でできることを正確に実行することに気づきました。

PHPチームジェネレーター

// Only PHP 5.5
function gen_one_to_three() {
    for ($i = 1; $i <= 3; $i++) {
        // Note that $i is preserved between yields.
        yield $i;
    }
}

$generator = gen_one_to_three();
foreach ($generator as $value) {
    echo "$value\n";
}

結果

1
2
3

しかし、配列を使用して同じことを行うことができます。また、以前のバージョンのPHPとの互換性を維持できます。

見てください

// Compatible with 4.4.9!
function gen_one_to_three() {
    $results = array();
    for ($i = 1; $i <= 3; $i++) {
        $results[] = $i;
    }

    return $results;
}

$generator = gen_one_to_three();
foreach ($generator as $value) {
    echo "$value\n";
}

それで問題は:この新機能の存在の目的は何ですか?新しい機能を使用せずに、ドキュメントのすべての例を再生して、配列に置き換えました。

誰かが良い説明とおそらく古いバージョンでは必ずしも不可能ではない例を与えることができますが、ジェネレーターを使用すると開発に役立ちますか?

38
David Rodrigues

違いは効率の点です。たとえば、PHP以外の多くの言語には2つのrange関数range()xrange()が含まれています。これはジェネレーターとその使用理由独自のジェネレーターを作成しましょう:

function range($start, $end) {
    $array = array();
    for ($i = $start; $i <= $end; $i++) {
        $array[] = $i;
    }
    return $array;
}

今ではそれは本当に簡単です。ただし、範囲が広い場合は、膨大な量のメモリが必要になります。 $start = 0$end = 100000000を使用して実行しようとすると、メモリが不足する可能性があります。

しかし、ジェネレーターを使用した場合:

function xrange($start, $end) {
    for ($i = $start; $i <= $end; $i++) {
        yield $i;
    }
}

現在、定数メモリを使用していますが、同じスペースで反復できる(そして他のイテレータで使用できる)「配列」(構造のような)がまだあります。

配列を置換しませんが、メモリの必要性を回避する効率的な方法を提供します...

しかし、それはまた、アイテムの生成に関して節約を提供します。各結果は必要に応じて生成されるため、必要になるまで各要素の実行(フェッチまたは計算)を遅らせることができます。したがって、たとえば、データベースからアイテムをフェッチし、各行の周りで複雑な処理を行う必要がある場合、実際にその行が必要になるまで、ジェネレーターを使用してそれを遅らせることができます。

function fetchFromDb($result) {
    while ($row = $result->fetchArray()) {
        $record = doSomeComplexProcessing($row);
        yield $record;
    }
}

したがって、最初の3つの結果のみが必要な場合は、最初の3つのレコードのみを処理します。

詳細については、この正確な主題について ブログ投稿 を書きました。

62
ircmaxell

ジェネレーターは、複雑なステートメントの遅延評価を可能にします。そうすれば、すべてを一度に割り当てる必要がないため、メモリを節約できます。

両方が反復可能であることに加えて、それらはほとんど同じではありません。 arrayはデータ構造ですが、ジェネレーターはそうではありません。

13
TimWolla

配列には、ループを開始する前に、ループしている各値が含まれている必要があります。ジェネレータは、要求に応じて「オンザフライ」で各値を作成するため、メモリが大幅に削減されます。

配列は、含まれている値で機能し、それらの値を事前に入力する必要があります。ジェネレーターは、直接使用される特別な基準に従って値を作成できます...例:フィボナッチ数列、または非A-Zアルファベットの文字(UTF-8数値で計算)により、alphaRange( 'א'、 'ת');が効果的に許可されます。

[〜#〜]編集[〜#〜]

function fibonacci($count) {
    $prev = 0;
    $current = 1;

    for ($i = 0; $i < $count; ++$i) {
        yield $prev;
        $next = $prev + $current;
        $prev = $current;
        $current = $next;
    }
}

foreach (fibonacci(48) as $i => $value) {
    echo $i , ' -> ' , $value, PHP_EOL;
}

[〜#〜]編集[〜#〜]

楽しみのために、ヘブライ語のアルファベットをUTF-8文字として返すジェネレーターを次に示します。

function hebrewAlphabet() {
    $utf8firstCharacter = 1488;
    $utf8lastCharacter = 1514;
    for ($character = $utf8firstCharacter; $character <= $utf8lastCharacter; ++$character) {
        yield html_entity_decode('&#'.$character.';', ENT_NOQUOTES, 'UTF-8');
    };
}

foreach(hebrewAlphabet() as $character) {
    echo $character, ' ';
}
12
Mark Baker

Pythonの場合:

アイテムのセットに対する反復がforステートメントの使用を開始すると、ジェネレーターが実行されます。ジェネレーターの関数コードが「yield」ステートメントに達すると、ジェネレーターは実行をforループに戻し、セットから新しい値を返します。ジェネレーター関数は、必要な数の値(場合によっては無限)を生成して、それぞれを順番に生成できます。

...ジェネレーターはyieldステートメントを一度に1つずつ実行し、その間に一時停止して、実行をメインのforループに戻します。

-learnpython.org

2
Luigi Siri