連載
» 2017年10月30日 05時00分 公開

Web業界で働くためのPHP入門(12):PHPのジェネレータ――foreachループで使える値生成のための関数 (1/3)

オープンソースのWeb開発向けスクリプト言語「PHP」の文法を一から学ぶための入門連載。今回は、「ジェネレータ」について解説します。

[齊藤新三(著)/山田祥寛(監修),WINGSプロジェクト]

 オープンソースのWeb開発向けスクリプト言語「PHP」の文法を一から学ぶための入門連載「Web業界で働くためのPHP入門」。今回は、「ジェネレータ」について解説します。

PHP 5.5から導入された「ジェネレータ」とは

 ジェネレータは、PHP 5.5から導入されたものです。ソースコードとしては関数とよく似ていますが、働きが全く違います。関数と比べて何が違うのか、ジェネレータとは何か、から話を始めていきましょう。

関数内でreturn以降の行は処理されない

 まず、関数のソースコードから確認していきましょう。下記のuseFunction.phpを作成し、実行してください。なお、ここで定義したreturnValues()関数は、通常ではあり得ない記述方法をしています。これは、後に記載するジェネレータとの違いをはっきりさせるためです。

<?php
function returnValues(): int
{
    $num = 8;
    $ans = $num * 3;
    return $ans;
    $ans = $num * 5;
    return $ans;
    $ans = $num * 7;
    return $ans;
}
 
$value1 = returnValues();
print("1回目: ".$value1);
$value2 = returnValues();
print("<br>2回目: ".$value2);
$value3 = returnValues();
print("<br>3回目: ".$value3);
リスト1 phplesson/chap12/useFunction.php

 実行結果は下記の通りです。

1回目: 24
2回目: 24
3回目: 24

 ソースコードとしては特に難しいものはありません。13〜18行目の実行処理部分でreturnValues()関数を3回呼び出しています。実行結果から分かるように何回呼び出しても、返却する値は「24」と5行目の結果$ansです。この$ansをreturnしているのが次の6行目です。7行目以降は全く処理されません。

図1 関数returnValues()内の処理

 returnが記述された行以降のコードは処理されませんので、条件分岐でもない限りは、関数内ではreturnは通常1回しか記述しません。

ジェネレータは値を次々と生成するもの

 リスト1と似たようなソースコードで、下記のuseGenerator.phpを作成し、実行してください。

<?php
function yieldValues()
{
    $num = 8;
    $ans = $num * 3;
    yield $ans;
    $ans = $num * 5;
    yield $ans;
    $ans = $num * 7;
    yield $ans;
}
 
$values = yieldValues();
foreach($values as $value) {
    print($value."<br>");
}
リスト2 phplesson/chap12/useGenerator.php

 実行結果は下記の通りです。

24
40
56

 yieldValues()はリスト1の関数returnValues()とほぼ同じです。違うのは、returnの代わりに、「yield」という見慣れないキーワードが使われています。yieldは、returnと同じようにその次に書かれた値、例えば、6行目なら$ansを呼び出し元に返します。ただし、returnとは全く違う動きをします。このyieldが書かれたfunctionブロックは、functionで始まっていますが通常の関数ではありません。これがジェネレータ関数です。なお、yieldは「産出する」という意味の英単語です。

 では、通常の関数とどのように違うのでしょうか。

 13〜16行目の実行部分の記述については後で解説しますが、全体としては、yieldValues()を次々呼び出しながら、その戻り値を表示させている処理です。その前提で実行結果を見てください。1回目の呼び出しで6行目のyield、2回目の呼び出しで8行目のyield、3回目の呼び出しで10行目のyieldの値を表示させています。

図2 ジェネレータyieldValues()内の処理

 これは、1回目の呼び出しで最初のyield(6行目)までを実行し、2回目の呼び出しでその続き(7行目)から次のyield(8行目)までを実行、3回目の呼び出しでさらにその続き(9行目)から次のyield(10行目)までを実行するというものです。

 このように、yieldを使うと前回の呼び出しでどこまで処理をしたかを記録しておき、次の呼び出しではその続きから次のyieldまで処理を行います。これは、呼び出す側からすると、次から次へと値が文字通り「産出」されているように見えます。つまり、「ジェネレータ=発生器」なのです。

ジェネレータはループで使用する

 次に、このジェネレータの使い方を解説します。これは、実行部分です。ジェネレータを利用する場合、通常関数のように下記のような記述方法はできません。

$value1 = yieldValues();

 ジェネレータは次から次へと値を生成するものなので、「foreach」ループとともに使います。その際、あらかじめ下記のようにジェネレータ関数を一度呼び出し、その実行結果を利用します()。

$values = yieldValues();

難しいことをいえば、このジェネレータ関数の実行結果として反復処理可能なオブジェクトが返されます。オブジェクトという概念は次回以降で扱います。

 foreachのループ対象としてこのジェネレータ関数の実行結果($values)を指定します。「as」の次に書かれた変数($value)にyieldされた値が次々代入されます。そして、yieldする値がなくなった時点で、foreachループが終了します。

図3 yieldValues()をforeachで処理

 なお、yieldValues()の実行結果を$valuesにいったん格納せずに下記のように直接foreachに記述しても構いません。

foreach(yieldValues() as $value)

 後は、ループブロック内でこの変数を使って値を処理していきます。リスト2では単にprint()しているだけです。

       1|2|3 次のページへ

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。