php5.5新機能「ジェネレータ」初心者入門

63
copyright(c) 2012 kuwata-lab.com all rights reserved. PHP5.5新機能かもしれない Generator 初心者入門 makoto kuwata <[email protected]> http://www.kuwata-lab.com/ 2012-09-15 (Sat) PHPカンファレンス2012

Upload: kwatch

Post on 21-May-2015

32.880 views

Category:

Technology


0 download

DESCRIPTION

PHP5.5の新機能「ジェネレータ(Generator)」について、「それって何?」「どううれしいの?」「何に使えるの?」の3つを初心者向けに解説。動画 http://www.slideshare.net/kwatch/php55

TRANSCRIPT

Page 1: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

PHP5.5新機能かもしれない

Generator 初心者入門makoto kuwata <[email protected]>http://www.kuwata-lab.com/2012-09-15 (Sat)

PHPカンファレンス2012

Page 2: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

本発表について• PHP5.5の新機能かもしれない「ジェネレータ」を、「なんだか凄そうだ」と思ってもらう。

• ジェネレータって何?• どううれしいの?• どんなことに使えるの?

• 内容は2012-09-15時点での情報に基づく。今後、仕様変更があり得るので注意。

【目的】

【内容】

【注意】

Page 3: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータって何?What is Generator?

Page 4: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

まとめ

ジェネレータ ⇒ セーブ機能

ジェネレータ関数 ⇒ ゲームシナリオ

ジェネレータオブジェクト ⇒ 冒険の書 (セーブデータ)

yield文 ⇒ 宿屋(セーブポイント)

Page 5: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

通常の関数

1: function func() { 2: $i = 0; 3: return $i; 4: $i++; 5: return $i; 6: $i++; 7: return $i; 8: }

毎回先頭から実行され、またreturn文より後ろは実行されない

1, 2, 3回目 (0が返される)

Page 6: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータ (Generator) 関数

1: function gfunc() { 2: $i = 0; 3: yield $i; 4: $i++; 5: yield $i; 6: $i++; 7: yield $i; 8: }

前回の終了位置から再開

1回目 (0が返される)

2回目 (1が返される)

3回目 (2が返される)

Page 7: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

使い方

1: $g = gfunc(); 2: foreach ($g as $x) { 3: var_dump($x); 4: }

ジェネレータオブジェクトを生成(通常の関数と使い方が違うことに注意!)

foreach文とともに使用(イテレータとして振る舞う)

int(0)int(1)int(2)

実行例

Page 8: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

実行順序:ループ1回目

$g = gfunc();

foreach($g as $x){

var_dump($x);

}

echo "done\n";

function gfunc(){ $i = 0; yield $i; $i++; yield $i; $i++; return $i;}

メインプログラム ジェネレータ関数

1

2 3

45

Page 9: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

実行順序:ループ2回目

$g = gfunc();

foreach($g as $x){

var_dump($x);

}

echo "done\n";

function gfunc(){ $i = 0; yield $i; $i++; yield $i; $i++; return $i;}

メインプログラム ジェネレータ関数

6

97

8

Page 10: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

実行順序:ループ3回目…は、ない

$g = gfunc();

foreach($g as $x){

var_dump($x);

}

echo "done\n";

function gfunc(){ $i = 0; yield $i; $i++; yield $i; $i++; return $i;}

メインプログラム ジェネレータ関数

・ループのたびにyield文まで実行・yield文の引数がループ変数に

10

11

12

13

Page 11: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

サンプル:2つの値を交互に出力

1: function toggle($odd, $even) { 2: while (TRUE) { 3: yield $odd; 4: yield $even; 5: } 6: } 7: 8: // "red" と "blue" を交互に出力 9: foreach (toggle("red", "blue") as $c){10: echo $c, "\n"; // 無限に出力11: }

Page 12: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

サンプル:フィボナッチ数列 (0, 1, 1, 2, 3, 5, 8, 13,...)

1: function fib() { 2: $x = 0; $y = 1; 3: while (TRUE) { 4: yield $x; 5: list($x, $y) = [$y, $x+$y]; 6: } 7: } 8: 9: // 100未満のフィボナッチ数列を出力10: foreach (fib() as $x) {11: if ($x >= 100) break;12: echo $x, "\n";13: }

コツ:ループの終了条件を指定しない (無限ループ)

コツ:終了条件は呼び出す側で指定する

Page 13: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

まとめ

ジェネレータ ⇒ セーブ機能

ジェネレータ関数 ⇒ ゲームシナリオ

ジェネレータオブジェクト ⇒ 冒険の書 (セーブデータ)

yield文 ⇒ 宿屋(セーブポイント)

Page 14: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

どううれしいの?Why Generator is so useful?

Page 15: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

Before: ファイルを1行ずつ処理する

1: // 行番号つきで表示 2: $f = fopen($filename, 'r'); 3: if ($f === FALSE) throw ....; 4: $line = fgets($f); 5: while ($line !== FALSE) {

6: ++$i; 7: echo $i, ": ", $line;

8: $line = fgets($f); 9: }10: fclose($f);11:

Page 16: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

Before: ファイルを1行ずつ処理する

1: // パターンで絞り込む 2: $f = fopen($filename, 'r'); 3: if ($f === FALSE) throw ....; 4: $line = fgets($f); 5: while ($line !== FALSE) {

6: if (preg_match('/@/', $line)) 7: echo $line;

8: $line = fgets($f); 9: }10: fclose($f);11:

Page 17: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

Before: ファイルを1行ずつ処理する

1: // タブ文字でフィールドに分解 2: $f = fopen($filename, 'r'); 3: if ($f === FALSE) throw ....; 4: $line = fgets($f); 5: while ($line !== FALSE) {

6: $arr = explode("\t", $line); 7: echo $arr[1], "\n";

8: $line = fgets($f); 9: }10: fclose($f);11:

汎用性の高いコードの中に汎用性の低いコードが混在

Page 18: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

After: ジェネレータ関数

1: 2: $f = fopen($filename, 'r'); 3: if ($f === FALSE) throw ....; 4: $line = fgets($f); 5: while ($line !== FALSE) {

6: $arr = explode("\t", $line); 7: echo $arr[1];

8: $line = fgets($f); 9: }10: fclose($f);11:

yield $line;

汎用性の低い箇所を yield 文に

function each_line($filename) {

}

汎用性の高い箇所を関数に抽出

Page 19: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

After: メインプログラム

1: // ジェネレータオブジェクトを生成 2: $g = each_line($filename); 3: // メインループ 4: foreach ($g as $line) { 5: // 汎用性の低い処理 6: $arr = explode("\t", $line); 7: echo $arr[1], "\n"; 8: }

Page 20: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータの利点

ループ処理から、汎用性の高い箇所だけを切り出せる(再利用性の向上)

Page 21: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

もっとジェネレータ関数

1: // ジェネレータオブジェクトを作成 2: $g = each_line($filename); 3: // ループ 4: foreach ($g as $line) { 5: $arr = explode("\t", $line); 6: echo $arr[1]; 7: } 8:

yield $arr;

function each_fields($g) {

}

受け取る

配列やイテレータでも可

ジェネレータオブジェクトを受け取り、新しい別のジェネレータオブジェクトを生成する

Page 22: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータを「重ねる」

1: // ジェネレータオブジェクトを生成

2: $g = each_line($filename); 3: 4: // メインループ

5: foreach ($g as $line) { 6: $arr = explode("\n", $line); 7: echo $arr[1], "\n"; 8: }

                            $arr

$g = each_fields($g); ジェネレータから別のジェネレータを生成

Page 23: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

1つの大きなループ vs. 複数の小さなループ

$f = fopen($filename, 'r');$line = fgets($f);while ($line !== FALSE) { $arr = explode("\n",$line); echo $arr[1]; $line = fgets($f);}fclose($f);

while ($line !== FALSE) { yield $line;}

$g = each_line($filename);$g = each_fields($g);foreach ($g as $arr) { echo $arr[1], "\n";}

foreach ($g as $line) { yield $arr;}

ジェネレータ使用前 ジェネレータ使用後

Page 24: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータの利点

ループ処理から、汎用性の高い箇所だけを切り出せる(再利用性の向上)

ひとつの大きなループを、複数の小さなループに分解できる(ループの簡素化とPipeline化)

Page 25: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

従来方法との比較:配列にすべて格納する

1: function each_line($filename) { 2: $f = fopen($filename, 'r'); 3: $lines = array(); 4: $line = fgets($f); 5: while ($line !== FALSE) { 6: $lines[] = $line; 7: $line = fgets($f); 8: } 9: fclose($f);10: return $lines;11: }

メモリを大量に消費(巨大データだと落ちる)

すべてを読み込まないと結果が返ってこない

Page 26: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

従来方法との比較:ジェネレータ

1: function each_line($filename) { 2: $f = fopen($filename, 'r'); 3: 4: $line = fgets($f); 5: while ($line !== FALSE) { 6: yield $line; 7: $line = fgets($f); 8: } 9: fclose($f);1011: }

1度に1行しか読み込まない(巨大なデータでも落ちない)

読み込んだはしから値を返す(ストリーム処理に最適)

Page 27: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

リダイレクト v.s. パイプライン

bash% command1 < input > tmp1bash% command2 < tmp1 > tmp2bash% command3 < tmp2

すべてを配列に格納する ≒「リダイレクト」・巨大な中間ファイルが必要・最後まで処理しないと何も出力されない

Page 28: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

リダイレクト v.s. パイプライン

bash% cat input | command1 \ | command2 \ | command3

ジェネレータを連結する ≒ 「パイプ」・巨大な中間ファイルがいらない・読み込んだはしから出力される

Page 29: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータの利点

ループ処理から、汎用性の高い箇所だけを切り出せる(再利用性の向上)

ひとつの大きなループを、複数の小さなループに分解できる(ループの簡素化とPipeline化)

メモリ消費量が少ない(巨大なデータを扱ってもプロセスが落ちない)

データを読んだはしから処理できる(ストリームデータも処理可能)

Page 30: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

どんな使い道があるの?Advanced Generator

Page 31: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータとインタラクション

ジェネレータ関数

メインプログラム

yield $valueforeach(){}

$g->send($arg)

メインプログラムからジェネレータ関数に値を渡せる

ジェネレータ関数からメインプログラムに値を返す

$g->send() … 次のyield文まで実行する

Page 32: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータとインタラクション

$value = $g->send("arg");

$arg = (yield "value");

ジェネレータ関数

メインプログラム

send()の引数がyield文の値に

yield文の引数がsend()の戻り値に

双方向への値の受け渡しが可能に

Page 33: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータとインタラクション

$g = gfunc();

$ret = $g->send(1);

$ret = $g->send(2);

$ret = $g->send(3);

function gfunc(){ $arg = yield; while (条件式) { $ret = ...; $arg = (yield $ret); var_dump($arg); }}

メインプログラム ジェネレータ関数

1

2 3

4

5

6

Page 34: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータとインタラクション

$g = gfunc();

$ret = $g->send(1);

$ret = $g->send(2);

$ret = $g->send(3);

function gfunc(){ $arg = yield; while (条件式) { $ret = ...; $arg = (yield $ret); var_dump($arg); }}

メインプログラム ジェネレータ関数

7

8

9

10

11

Page 35: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータとインタラクション

$g = gfunc();

$ret = $g->send(1);

$ret = $g->send(2);

$ret = $g->send(3);

function gfunc(){ $arg = yield; while (条件式) { $ret = ...; $arg = (yield $ret); var_dump($arg); }}

メインプログラム ジェネレータ関数

12

13

1415

16

Page 36: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

サンプル:数字あてゲーム

1: function guess_quiz($num) {2: $ans = yield;3: while ($num != $ans) {4: if ($ans > $num)5: $ans = (yield "too large");6: else7: $ans = (yield "too small");8: }9: }

最初のsend()の引数値を変数に代入

値を返し、かつsend()の引数値を変数に代入

Page 37: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

サンプル:数字あてゲーム

1: $g = guess_quiz(mt_rand(1, 100));2: do {3: echo "guess number (1-100): ";4: $ans = fgets(STDIN, 128);5: if ($ans === false) break;6: $hint = $g->send($ans);7: echo $hint ? $hint."\n"8: : "Correct!\n";9: } while ($hint); 値を送信し、かつ

次のyield文まで実行

Page 38: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータとマルチスレッド

Process

Native Thread

Green Thread

Generator

リソース消費量

高機能

機能は限られるがメモリ消費量が極めて少ない(=大量生成可能)

: OSの機能として実現: 言語やライブラリで実現

Page 39: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータと非同期処理

処理1(function($data) { 処理2(function($data) { 処理3(function($data) { .... }); });});

ネストしたコールバック関数

読みにくい、書きにくい、わかりにくい

Page 40: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータと非同期処理

$d = new Deferred();$d->next(function($data) { ..処理1..;})->next(function($data) { ..処理2..;})->next(function($data) { ..処理3..;});

コールバック関数を数珠つなぎ

記述量が多い、書き方が不自然

Page 41: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータと非同期処理

function doSomething() { $data = yield; ...処理1...; $data = yield; ...処理2...; $data = yield; ...処理3...;}

ジェネレータ

自然な書き方でわかりやすい!

Page 42: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

ジェネレータとページ遷移

1: $response = フォームを表示();

2: $request = (yield $response);

3: $response = プレビューを表示($request);

4: $request = (yield $response);

5: データベースに登録($request);

複数ページにまたがる遷移を非同期処理と同じように記述(詳しくは「継続ベース フレームワーク」でggr)

※ Apache だとリクエストごとにすべてをリセットするので、実現できない。 PHP のビルトイン Web サーバのようなパーシステントプロセスのサーバを使って、 リクエストを超えてジェネレータオブジェクトを保持できるような仕組みが必要。

Page 43: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

落とし穴:breakされた場合

1: function each_line($filename) { 2: $f = fopen($filename, 'r'); 3: $line = fgets($f); 4: while ($line !== FALSE) { 5: yield $line; 6: $line = fgets($f); 7: } 8: fclose($f); 9: }

※ SPLFileObject も同じ問題を抱えているが、デストラクタで fclose() している。

呼び出し側でbreakされると終了処理が行われない!

(しかもPHPにはfinallyがないorz)※

Page 44: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

落とし穴:リファクタリング

1: function stepping($arr) { 2: $n = count($arr); 3: for ($i=0; $i<$n; $i+=2) 4: yield $arr[$i]; 5: for ($i=1; $i<$n; $i+=2) 6: yield $arr[$i]; 7: }

奇数番目をyieldしてから、偶数番目をyieldする

DRYじゃない!関数化しよう!

Page 45: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

落とし穴:リファクタリング

1: function _sub($arr, $i, $n) { 2: for (; $i<$n; $i+=2) 3: yield $arr[$i]; 4: } 5: function stepping($arr) { 6: $n = count($arr); 7: _sub($arr, 0, $n); 8: _sub($arr, 1, $n); 9: }

DRYになった、けど動かない!

ジェネレータ関数を呼び出しているがforeach文を使ってない

Page 46: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

落とし穴:リファクタリング

1: function _sub($arr, $i, $n) { 2: for (; $i<$n; $i+=2) 3: yield $arr[$i]; 4: } 5: function stepping($arr) { 6: $n = count($arr); 7: foreach (_sub($arr, 0, $n) as $x) 8: yield $x; 9: foreach (_sub($arr, 1, $n) as $x)10: yield $x;11: }

動くようになった…けどなんか腑に落ちない

Page 47: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

まとめ

$->send()を使うと、双方向での値の受け渡しが可能マルチスレッドよりも低機能だが軽量非同期処理が自然な形で記述できる落とし穴もあるよ!

Page 48: PHP5.5新機能「ジェネレータ」初心者入門

any questions?

Page 49: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

おまけMore Things

Page 50: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

おまけ:PHP5.5 コンパイル方法

$ git clone \ https://github.com/nikic/php-src.git$ cd php-src/$ git checkout -b addGeneratorSupport \ origin/addGeneratorSupport$ ./buildconf$ apxs2=/usr/local/apache2/bin/apxs$ ./configure --with-apxs2=$apxs2$ nice -20 time make$ sapi/cli/php myexample.php

Page 51: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

おまけ:インデックスつきyield

1: function gfunc() { 2: yield 'a'; 3: yield 'b'; 4: yield 99=>'c'; 5: } 6: 7: $g = gfunc(); 8: foreach ($g as $k=>$v) { 9: echo "$k => $v \n";10: }

// 実行結果0 => a1 => b99 => c

Page 52: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

おまけ:参照渡しでのyield

1: function &gfunc(&$arr) { 2: foreach ($arr as &$x){ 3: yield $x; 4: } 5: } 6: 7: $arr = [10, 20, 30]; 8: $g = gfunc($arr); 9: foreach ($g as &$x)10: $x += 1;11: var_dump($arr);

// 実行結果array(3) { [0]=> int(11) [1]=> int(21) [2]=> &int(31)}

Page 53: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

おまけ:クロージャとの比較

function fib() { list($x, $y) = [0, 1]; return function() use ($x, $y) { $tmp = $x; list($x, $y) = [$y, $x+$y]; return $tmp; };}

// 使い方$closure = fib();$x = $closure();while ($x < 100) { echo $x, "\n"; $x = $closure();}

それ、クロージャでもできるよ!

Page 54: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

おまけ:クロージャとの比較

function fib() { list($x, $y) = [0, 1]; return function() use ($x, $y) { $tmp = $x; list($x, $y) = [$y, $x+$y]; return $tmp; };}

function fib() { list($x, $y) = [0, 1]; while (TRUE) { yield $x; list($x, $y) = [$y, $x+$y]; }}

クロージャ版 ジェネレータ版

・毎回先頭から実行される制約・すべてをreturnの前に書かなければならない制約

・前回の終了場所から自動的に再開 (より自然な記述)・yieldの後ろにも処理が書ける (より自然な記述)

Page 55: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

おまけ:内部イテレータとの比較

function fib($fn) { list($x, $y) = [0, 1]; while ($x < 100) { $fn($x); list($x, $y) = [$y, $x+$y]; }}

// 使い方fib(function($x) { echo $x, "\n";});

それ、内部イテレータでもできるよ!

Page 56: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

おまけ:内部イテレータとの比較

function fib($c,$fn){ list($x, $y) = [0, 1]; while ($c($x)) { $fn($x); list($x, $y) = [$y, $x+$y]; }}

// 使い方fib( function($x) { return $x < 100; }, function($x) { echo $x, "\n"; });

ループの終了条件も指定したい場合は、非常にブサイク

ボディ部の両方が必要

終了条件と…

Page 57: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

おまけ:内部イテレータとの比較

function fib(){ list($x, $y) = [0, 1]; while (TRUE) { yield $x; list($x, $y) = [$y, $x+$y]; }}

// 使い方foreach(fib() as $x){ // 終了条件 if ($x >= 100) break; // ボディ部 echo $x, "\n";};

可変箇所が複数ならジェネレータのほうがよっぽどきれい

Page 58: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

おまけ:内部イテレータとの比較

def fib() x, y = 0, 1 while true yield x x, y = y, x+y endend

// 使い方

fib {¦x¦ // 終了条件

break if x >= 100 // ボディ部

puts x}

Rubyでは、1つの無名関数 (ブロック) で「終了条件」と「ボディ部」の両方を指定できる。

Page 59: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

おまけ:「継続 (Continuation)」との比較

継続のほうができることが広い、ジェネレータはそのサブセット継続はcall stackを丸ごとコピーする ので重い、ジェネレータはstack flame1つだけなので軽い継続は理解するのがすーーーっごく難しい、ジェネレータはわかりやすいし使いやすい

※処理系により実装方法は異なる場合がある

Page 60: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

おまけ:ベンチマーク

Loop

Array

Generator

Inner Iterator

Closure

0 0.2 0.4 0.6 0.8

配列に詰め込むのは高コスト

(sec)

PHP: 5.5-addGeneratorSupportOS: MacOSXCPU: Core2DUO 2GHz

Code: https://gist.github.com/3710544

Page 61: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

おまけ:ベンチマーク

Loop

Generator

G + explode()

0 0.5 1 1.5 2

G + explode() + array()

ジェネレータは十分に低コスト

ループ内処理 (explode()やarray()) のほうがよっぽど高コスト

(sec)

PHP: 5.5-addGeneratorSupportOS: MacOSXCPU: Core2DUO 2GHz

Code: https://gist.github.com/3710569

Page 62: PHP5.5新機能「ジェネレータ」初心者入門

copyright(c) 2012 kuwata-lab.com all rights reserved.

おまけ:参考文献

What PHP 5.5 might look likehttp://nikic.github.com/2012/07/10/What-PHP-5-5-might-look-like.html

Request for Comments: Generatorshttps://wiki.php.net/rfc/generators

Scheme/継続の種類と利用例http://ja.wikibooks.org/wiki/Scheme/継続の種類と利用例

Vallog - 継続の実装方針http://valvallow.blogspot.jp/2011/01/blog-post_11.html

Twisted Intro: 「コールバック」ではない方法http://skitazaki.appspot.com/translation/twisted-intro-ja/p17.html

境界を越える: 継続とWeb開発、そしてJavaプログラミングhttp://www.ibm.com/developerworks/jp/java/library/j-cb03216/index.html

Page 63: PHP5.5新機能「ジェネレータ」初心者入門

おしまい