ジェネレータが何かを理解できたところで、少しずつ応用させていきます。まず、配列をループ処理する場合を考えます。まずは、下記のloopArray.phpを作成し、実行してください。
<?php $array = [1, 3, 5, 7]; foreach($array as $value) { $ans = $value * 8; print($ans."<br>"); }
実行結果は下記の通りです。
8 24 40 56
特に説明の必要はないでしょう。2行目で配列を用意し、それを3行目以降でループ処理しています。
これと同じ処理を、ジェネレータを使って実現できます。下記のloopGenerator.phpを作成し、実行してください。
<?php function yieldArrayElement() { $array = [1, 3, 5, 7]; yield $array[0]; yield $array[1]; yield $array[2]; yield $array[3]; } foreach(yieldArrayElement() as $value) { $ans = $value * 8; print($ans."<br>"); }
実行結果はリスト3と同じです。リスト4は配列を直接ループ処理しています。
一方、リスト4では配列を、ジェネレータを使って1つずつyieldしながら処理しています。
これら、2つは同じ結果を得られます。
さらに、リスト4のyieldArrayElement()では配列内の要素を、インデックスを使ってyieldしていますが、通常このような記述はせずにループで処理します。リスト4の改良版である下記のloopGenerator2.phpを作成し、実行してください。
<?php function yieldArrayElement() { $array = [1, 3, 5, 7]; foreach($array as $element) { yield $element; } } foreach(yieldArrayElement() as $value) { $ans = $value * 8; print($ans."<br>"); }
実行結果はリスト4と同じです。
ジェネレータ関数yieldArrayElement()内で
yield $array[0];
となっていたものが、
yield $element;
とループ処理に置き換わっただけですが、このように記述するのが普通です。
このように、配列のループがジェネレータを使って代用できることが理解できたと思いますが、そもそも、この方式のメリットが分からないと思います。ソースコードでいえば、リスト5の方が圧倒的に簡素です。では、配列そのものが、リスト5のように固定データではなく、ループで生成するとしたらどうなるでしょうか。
例えば、指定の個数だけ1〜10の乱数が格納された配列を用意し、その配列を表示させながら合計値を計算する処理を考えます。下記のuseRandLoop.phpのようになります。このファイルを作成し、実行してください。なお、ここでは、配列の要素数として10個を指定しています。
<?php function createRandArray(int $count): array { $array = []; for($i = 1; $i <= $count; $i++) { $array[] = rand(1, 10); } return $array; } $sum = 0; foreach(createRandArray(10) as $value) { print("現在の乱数: ".$value."<br>"); $sum += $value; } print("乱数の合計: ".$sum);
実行結果は下記の通りです。なお、乱数を使用するので、実行結果は毎回変わります。
現在の乱数: 1 現在の乱数: 10 現在の乱数: 2 現在の乱数: 10 現在の乱数: 8 現在の乱数: 8 現在の乱数: 6 現在の乱数: 3 現在の乱数: 4 現在の乱数: 6 乱数の合計: 58
ここでは通常の関数createRandArray()を使って乱数が格納された配列を生成しています。その配列を使って、表示、および合計値の計算を行うループ処理を11行目以降で行っています。このソースコードの問題点は、createRandArray()関数でいったん配列を生成していることです。生成された配列はその個数分だけメモリを必要とします。ここでは要素数が10コの配列なので問題ないですが、要素数が100万個となると、100MBを超えるメモリを使うことになります。
ところが、この問題点はジェネレータを使うことで解決できます。リスト6と同じ処理を、ジェネレータを使って実現する下記のuseRandGenerator.phpを作成し、実行してください。
<?php function randGenerator($count) { for($i = 1; $i <= $count; $i++) { yield rand(1, 10); } } $sum = 0; foreach(randGenerator(10) as $value) { print("現在の乱数: ".$value."<br>"); $sum += $value; } print("乱数の合計: ".$sum);
実行結果は、乱数を使っているので値は変わっていますが、リスト6と同じようなものになります。
ここでのポイントは、randGenerator()ジェネレータ関数と、その内部で値をyieldしている5行目です。このジェネレータ関数では、その場で乱数を生成し、それをyieldしています。こうすることで、全部の値が格納された配列を用意する必要がなく、値がループされるたびに生成されるのでメモリの節約になります。これは、生成する値が多ければ多いほど、その恩恵にあずかれます。
Copyright © ITmedia, Inc. All Rights Reserved.