Search

検索したいワードを入力してください

2019年02月15日

PHPの便利なジェネレータ(yield構文)を使う方法を解説

PHPのジェネレータ関数について、使い方、通常の関数との違い、注意点を参考プログラムと出力結果とともに解説しています。大量のデータを扱う処理などにジェネレータ関数を活用すれば、メモリの消費量を節約でき、プログラムの可読性も上がります。

PHPのジェネレータ関数とは

PHPで関数を定義する際にfunctionの中でyield構文を使って値を生成している関数をジェネレータ関数と呼びます。またジェネレータ関数を使って生成されるGeneratorオブジェクトは繰り返し処理が可能なデータ構造のオブジェクトです。

通常のPHPの関数ではreturnを使うと値を返しますが、yieldを使ったジェネレータ関数では値を返すのではなく、Generatorオブジェクトを返します。yieldによって生成された値はそのオブジェクトの中に入っています。

次のプログラムでPHPの通常の関数normalFuncではreturnを使っているので文字列sampleを返しますが、ジェネレータ関数generatorFuncではGeneratorオブジェクトを返します。

function normalFunc() {
return 'sample';
}

function generatorFunc() {
yield 'sample';
}

var_dump(normalFunc());
var_dump(generatorFunc());

出力結果

string(6) "sample"
object(Generator)#1 (0) {
}

Generatorオブジェクトは配列のようなデータ構造なのでその中にはyieldによって生成された値が入っており、その値を取り出すためにループ処理を行います。

次のプログラムではyieldを使って、文字列appleを生成し、foreachを使って、Generatorオブジェクトから値を取り出します。

function generatorFunc() {
yield 'apple';
}

$yields = generatorFunc();
foreach ($yields as $yield) {
echo $yield;
}


出力結果

apple

ジェネレータのメリット

メモリの節約ができる?

繰り返し処理で配列を使って大量のデータを扱う場合、ジェネレータ関数を使うと配列の生成をしなくなる分メモリの消費量を抑えることができます。

PHPの通常の関数を使った場合とジェネレータ関数を使った場合のメモリ消費量を比較します。

ジェネレータを使わない場合の処理

PHPの配列に1から10000までの数値を代入する関数を用意して、その関数から出力された配列の値を取り出すプログラムを使います。

まずはジェネレータ関数を使わずに配列を生成して、値を代入する場合の例です。

ジェネレータなしのサンプルコード

function test() {
$arr = [];
for($i=0;$i<10000;$i++) {
$arr[] = $i;
}

return $arr;
}

echo "before:".memory_get_usage() / (1024 * 1024)."MB\n";

$result = test();

foreach ($result as $value) {}

echo "after:".memory_get_usage() / (1024 * 1024)."MB\n";

出力結果

before:0.36590576171875MB
after:0.86989593505859MB


test関数実行前と実行後を比べると、約0.5MBメモリの消費量が上がっています。

ジェネレータを使った場合の処理

次に上記のプログラムをPHPのジェネレータ関数に置き換えて、メモリの消費量を調べます。

returnの代わりにyieldを使用し、配列の生成を行いません。

ジェネレータを使ったサンプルコード

function test() {

for($i=0;$i<10000;$i++) {
yield $i;
}
}

echo "before:".memory_get_usage() / (1024 * 1024)."MB\n";

$result = test();
foreach ($result as $value) {}

echo "after:".memory_get_usage() / (1024 * 1024)."MB\n";

出力結果

before:0.36524963378906MB
after:0.36564636230469MB


PHPの通常の関数と比べると、ジェネレータ関数ではメモリの消費量を大幅に節約できています。またプログラムの見通しもよくなっています。

10000回配列に数値を代入するという処理でこれだけメモリ消費量に差が出ます。

yieldの使い方【基本編】

yieldの使い方は、通常の関数を定義する時に使われるreturnと似ていますが、挙動が違いますので注意してください。

returnを使った場合はその時点で関数の実行を終了して値を返します。

yieldを使った場合は関数の実行を一旦そこで止めて、関数の呼び出し元に値を返した後に関数の実行を再開します。yieldによって生成される値がなくなるまで関数は実行されます。

まずはreturnを使った通常の関数の例です。関数returnFuncを何度呼び出しても最初にreturnが記述してある箇所での変数ansの値を返します。

function returnFunc()
{
$num = 10;
$ans = $num * 2;
return $ans;
$ans = $num * 3;
return $ans;
$ans = $num * 4;
return $ans;
}

$val1 = returnFunc();
print("1回目: ".$val1 . "\n");
$val2 = returnFunc();
print("2回目: ".$val2 . "\n");
$val3 = returnFunc();
print("3回目: ".$val3 . "\n");

出力結果

1回目: 20
2回目: 20
3回目: 20

次にyieldを使ったジェネレータ関数の例です。

ループ1回目は1つ目のyieldの記述がある箇所の値を返し、2回目は2つ目のyieldの記述がある箇所、3回目は3つ目のyieldの記述がある箇所での変数ansの値を返しています。またyieldの記述がなくなるまで実行されています。

function generatorFunc()
{
$num = 10;
$ans = $num * 2;
yield $ans;
$ans = $num * 3;
yield $ans;
$ans = $num * 4;
yield $ans;
}

$vals = generatorFunc();
$i = 1;
foreach($vals as $val) {
print($i . "回目:" . $val . "\n");
$i++;
}

出力結果

1回目:20
2回目:30
3回目:40

yieldの使い方【応用編】

これまでのyieldを使った参考プログラムでは要素しか扱ってきませんでしたが、キーと要素をペアにして生成することもでき、連想配列のように扱うこともできます。

function generatorFunc()
{
for($i = 1; $i <= 10; $i++) {
yield $i => 10 * $i;
}
}

foreach(generatorFunc() as $index => $value) {
print($index . "個目: " . $value . "\n");
}

出力結果

1個目: 10
2個目: 20
3個目: 30
4個目: 40
5個目: 50
6個目: 60
7個目: 70
8個目: 80
9個目: 90
10個目: 100

また次のようにyieldを使って値を生成しなかった場合は要素が出力されません。iterator_to_array関数とvar_dump関数を使って、Generatorオブジェクトの型と値を出力すると、キーが割り振られており、NULL値が生成されています。

function generatorFunc() {
yield;
yield;
yield;
}

$yields = generatorFunc();
foreach ($yields as $yield) {
echo $yield;
}

var_dump(iterator_to_array(generatorFunc()));

出力結果

array(3) {
[0]=>
NULL
[1]=>
NULL
[2]=>
NULL
}

より実践に近いyieldの使い方として、1から1000まで順番に足していって、その合計値を出力する例です。

function test() {
for($i = 1; $i <= 1000; $i++) {
yield $i;
}
}

$sum = 0;
foreach(test() as $value) {
$sum += $value;
}

echo $sum;


出力結果

500500

まとめ

PHPのジェネレータ関数について解説しました。

繰り返し処理で配列を使って、大量にデータを扱っているプログラムなどにジェネレータ関数を使えばメモリの消費量を節約でき、プログラムの見通しもよくなります。

ジェネレータはPHPのバージョン5.5で追加された機能なので、導入を検討する際はPHPのバージョンにも気をつけてください。

【PR】多くの人がプログラミングを諦めてしまう理由をご存知ですか?



近年プログラミングを勉強する人が増えています。

プログラミング学習者の多くは独学から取り組もうとしますが、だいたい80%ほどは3ヶ月も続かずに諦めてしまいます。早い人は1日目で。

多くの人がプログラミングを独学しようとして諦める理由は、次の3つ。
●モチベーションが維持できない
●エラーの原因・解決方法が分からない
●どう学習すればよいか分からない

TechBoostというプログラミングスクールでは、みんなと一緒にプログラミングをするのでモチベーションの維持ができ、分からないことがあればマンツーマンで教えてくれ、徹底的に研究された初心者向けの教材が揃っています。

TechBoostを卒業後、実際にエンジニアとして転職した方もいるほど。

本気でプログラミングを学びたい方は、一度無料のカウンセリングでご相談ください。プログラミングを嫌いになる前に。

tech boostについて

オーダーメイド型の学習コンテンツを提供する「tech boost」 は、エンジニアのキャリア支援に特化したサービスを複数展開している株式会社Branding Engineerが運営しているプログラミングスクールです。最短3ヶ月間で、未経験から『プログラミングの基礎』、『実際に業務で必要となるスキル』、『今のトレンドとなっている知識』まで学べ、ご希望の方にはプロのキャリアアドバイザーによる就業支援を行うことができます。

tech boost卒業生インタビュー

tech boostの卒業生の声を聞きました。あなたがプログラミングを学びたい理由を、一度考えてみてください。
営業→Javaエンジニア→Rubyエンジニアと転向し、第一志望のFinTech企業で働く山下さん
元営業、ビジネスのわかるエンジニアを目指す菅原さん
サンフランシスコに交換留学し、シリコンバレーのVCでインターン中の梅本さん
予備校の営業から半年でエンジニア転職を果たした小田島さん

tech boostの口コミ



Related