29
27

More than 5 years have passed since last update.

PHP7調査(24)ジェネレータがyield fromとreturnをサポート

Posted at

ジェネレータはPHP5.5で導入された機能です。PHP7では、ジェネレータをさらに便利にするような文法としてyield fromreturnが実装されました。これはPython 3.3から文法ごと輸入したものです(たぶん)。それぞれ簡単に説明します。

yield from

PHP5では、ジェネレータ関数が値を返す構文はyieldしかありませんでした。PHP7では新たな構文が導入されます。

yield from <expr>

この文では<expr>から順に値を取り出し、それぞれの値をyieldします。<expr>にはTraversableなオブジェクトまたは配列を指定することができます。

<?php
function gen2($i) {
  yield $i;
  yield $i*2;
}

function gen1() {
  yield from [1,2];
  yield from gen2(4);
}

foreach (gen1() as $i) {
  var_dump($i);
}

上記プログラムを実行すると下記のような結果が得られます。

int(1)
int(2)
int(4)
int(8)

これは「ジェネレータ関数の処理の一部を別のジェネレータに委譲する」などと説明されることも多いようです。巨大で複雑なジェネレータ関数を作るような場合に、この構文で簡単に関数分割ができますね。

ジェネレータ関数でのreturn

PHP5までのジェネレータ関数にはreturnを書くことはできませんでした。PHP7からは、ジェネレータ関数にreturnを書くことができるようになりました。returnのタイミングでジェネレータ関数は終了します。また、returnした値はジェネレータの新たなメソッドgetReturn()で取得することができます。

<?php
function gen() {
  yield 1;
  yield 2;
  return 3;
}

$gen = gen();
foreach ($gen as $i) {
  var_dump($i);
}
echo $gen->getReturn(), "\n";

上記プログラムを実行すると下記のような結果が得られます。

int(1)
int(2)
3

この例だとreturnの存在理由がわからない程度ですが、yield from文と組み合わせると少し違った動きになります。yield from文でジェネレータを呼び出した場合、ジェネレータのreturn値がyield from文の評価値になります。

<?php
function main() {
    $gen = gen1(0);
    $x = $gen->current();
    printf("%d <--yield\n", $x);
    $y = $gen->send(2);
    printf("%d <--yield\n", $y);
    $z = $gen->send(5);
    printf("%d <--yield\n", $z);
    $w = $gen->send(7);
    printf("%d <--yield\n", $w);
    $gen->send(10);
    printf("%d <--return\n", $gen->getReturn());
}

function gen1($a) {
    printf("arg--> %d\n", $a);
    $b = yield 1;
    printf("send--> %d\n", $b);
    $c = yield from gen2(3);
    printf("%d <--return\n", $c);
    $d = yield 9;
    printf("send--> %d\n", $d);
    return 11;
}

function gen2($a) {
    printf("arg--> %d\n", $a);
    $b = yield 4;
    printf("send--> %d\n", $b);
    $c = yield 6;
    printf("send--> %d\n", $c);
    return 8;
}

main();

上記プログラムを実行すると下記のような結果が得られます。

arg--> 0
1 <--yield
send--> 2
arg--> 3
4 <--yield
send--> 5
6 <--yield
send--> 7
8 <--return
9 <--yield
send--> 10
11 <--return

yield fromで呼び出されているジェネレータ関数gen2()の返す値のうち、4と6はmain()が受け取り、8はgen1()が受け取っていることがわかります。このように、異なる2つの関数と値のやりとりができているのは特徴的だと言えます。

この例はかなり複雑なので、手元で試した方が理解が進むかもしれません。

感想など

将来的にはPythonのasyncioのようにyield fromを活用するライブラリが出てくると思いますが、それまでは謎の機能という扱いになりそうですね…。

参照

29
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
27