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

K
PHPカンファレンス2012


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




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




                    copyright(c) 2012 kuwata-lab.com all rights reserved.
本発表について
【目的】 • PHP5.5の新機能かもしれない「ジェネレータ」を、
       「なんだか凄そうだ」と思ってもらう。


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


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


          copyright(c) 2012 kuwata-lab.com all rights reserved.
ジェネレータって何?
What is Generator?




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

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


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


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

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


           copyright(c) 2012 kuwata-lab.com all rights reserved.
通常の関数

	 1:	 function	 func()	 {
	 2:	 	 	 	 $i	 =	 0;
                         1, 2, 3回目 (0が返される)
	 3:	 	 	 	 return	 $i;
	 4:	 	 	 	 $i++;
	 5:	 	 	 	 return	 $i;
	 6:	 	 	 	 $i++;
	 7:	 	 	 	 return	 $i;
	 8:	 }
                     毎回先頭から実行され、また
                   return文より後ろは実行されない


            copyright(c) 2012 kuwata-lab.com all rights reserved.
ジェネレータ (Generator) 関数

	 1:	 function	 gfunc()	 {
	 2:	 	 	 	 $i	 =	 0;
                        1回目 (0が返される)
	 3:	 	 	 	 yield	 $i;
	 4:	 	 	 	 $i++;
	 5:	 	 	 	 yield	 $i;  2回目 (1が返される)
	 6:	 	 	 	 $i++;
	 7:	 	 	 	 yield	 $i;  3回目 (2が返される)
	 8:	 }
           前回の終了位置から再開



           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)

             copyright(c) 2012 kuwata-lab.com all rights reserved.
実行順序:ループ1回目

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

1
  $g	 =	 gfunc();                            function	 gfunc(){
2
  foreach($g	 as	 $x){                       	 	 $i	 =	 0;
                                               3
  	 	 var_dump($x);                          	 	 yield	 $i;
                                               4
    5
  }                                          	 	 $i++;
  echo	 "donen";                            	 	 yield	 $i;
                                             	 	 $i++;
                                             	 	 return	 $i;
                                             }

              copyright(c) 2012 kuwata-lab.com all rights reserved.
実行順序:ループ2回目

メインプログラム                                    ジェネレータ関数
  $g	 =	 gfunc();                            function	 gfunc(){
6
  foreach($g	 as	 $x){                       	 	 $i	 =	 0;
  	 	 var_dump($x);                          	 	 yield	 $i;
    9
  }                                          	 	 $i++;
                                               7

  echo	 "donen";                            	 	 yield	 $i;
                                               8
                                             	 	 $i++;
                                             	 	 return	 $i;
                                             }

              copyright(c) 2012 kuwata-lab.com all rights reserved.
実行順序:ループ3回目…は、ない

 メインプログラム                                   ジェネレータ関数
  $g	 =	 gfunc();                            function	 gfunc(){
10foreach($g	 as	 $x){                       	 	 $i	 =	 0;
  	 	 var_dump($x);                          	 	 yield	 $i;
  }                                          	 	 $i++;
                                             	 	 yield	 $i;
13echo	 "donen";
                                             	 	 $i++;
                                             11
                                             	 	 return	 $i;
                                             12
・ループのたびにyield文まで実行
・yield文の引数がループ変数に
                                             }

              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:	 }


              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:	 }                                  び出す側で指定する

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

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


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


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

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


           copyright(c) 2012 kuwata-lab.com all rights reserved.
どううれしいの?
Why Generator is so useful?




                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:	 
               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:	 
              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:	 
              copyright(c) 2012 kuwata-lab.com all rights reserved.
After: ジェネレータ関数
                                            汎用性の高い箇所を関数に抽出

	 1: function	 each_line($filename)	 {
	 2:	 	 	 	 $f	 =	 fopen($filename,	 'r');
	 3:	 	 	 	 if	 ($f	 ===	 FALSE)	 throw	 ....;
	 4:	 	 	 	 $line	 =	 fgets($f);
	 5:	 	 	 	 while	 ($line	 !==	 FALSE)	 {
	 6:	 	 	 	 	 	 	 $arr	 =	 explode("t",	 $line);
                  yield	 $line;
	 7:	 	 	 	 	 	 	 echo	 $arr[1];
                        汎用性の低い箇所を yield 文に
	 8:	 	 	 	 	 	 	 $line	 =	 fgets($f);
	 9:	 	 	 	 }
10:	 	 	 	 fclose($f);
11:	 }
               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:	 }




              copyright(c) 2012 kuwata-lab.com all rights reserved.
ジェネレータの利点

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


◆


◆



      copyright(c) 2012 kuwata-lab.com all rights reserved.
もっとジェネレータ関数
          ジェネレータオブジェクトを受け取り、新し
          い別のジェネレータオブジェクトを生成する

	 1:	 	 	 	 //	 ジェネレータオブジェクトを作成            受け取る
        function	 each_fields($g)	 {
	 2:	 	 	 	 $g	 =	 each_line($filename);
	 3:	 	 	 	 //	 ループ                   配列やイテレータでも可
	 4:	 	 	 	 foreach	 ($g	 as	 $line)	 {
	 5:	 	 	 	 	 	 	 $arr	 =	 explode("t",	 $line);
	 6:	 	 	 	 	 	 	 echo	 $arr[1];
                  yield	 $arr;
	 7:	 	 	 	 }
	 8: }



             copyright(c) 2012 kuwata-lab.com all rights reserved.
ジェネレータを「重ねる」

	 1:	 //	 ジェネレータオブジェクトを生成
	 2:	 $g	 =	 each_line($filename);
	 3: $g	 =	 each_fields($g);         ジェネレータから
	 4:	 //	 メインループ                     別のジェネレータ
	 5:	 foreach	 ($g	 as	 $line)	 { を生成
                          $arr
	 6:	 	 	 	 $arr	 =	 explode("n",	 $line);  
                                            	 
	 7:	 	 	 	 echo	 $arr[1],	 "n";
	 8:	 }



             copyright(c) 2012 kuwata-lab.com all rights reserved.
1つの大きなループ vs. 複数の小さなループ

ジェネレータ使用前                                                  ジェネレータ使用後
$f	 =	 fopen($filename,	 'r');                              while	 ($line	 !==	 FALSE)	 {
$line	 =	 fgets($f);                                        	 	 yield	 $line;
while	 ($line	 !==	 FALSE)	 {                               }
	 	 $arr	 =	 explode("n",$line);	 
	 	 echo	 $arr[1];                                          foreach	 ($g	 as	 $line)	 {
	 	 $line	 =	 fgets($f);                                    	 	 yield	 $arr;
}                                                           }
fclose($f);
                                                            $g	 =	 each_line($filename);
                                                            $g	 =	 each_fields($g);
                                                            foreach	 ($g	 as	 $arr)	 {
                                                            	 	 echo	 $arr[1],	 "n";
                                                            }



                     copyright(c) 2012 kuwata-lab.com all rights reserved.
ジェネレータの利点

◆ ループ処理から、汎用性の高い箇所だけを切り
  出せる(再利用性の向上)
◆ ひとつの大きなループを、複数の小さなループ
  に分解できる(ループの簡素化とPipeline化)
◆


◆



        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:	 }

            copyright(c) 2012 kuwata-lab.com all rights reserved.
従来方法との比較:ジェネレータ

	 1:	 function	 each_line($filename)	 {
	 2:	 	 	 	 $f	 =	 fopen($filename,	 'r');
	 3:	 	 	 	 
                                 1度に1行しか読み込まない
	 4:	 	 	 	 $line	 =	 fgets($f); (巨大なデータでも落ちない)
	 5:	 	 	 	 while	 ($line	 !==	 FALSE)	 {
	 6:	 	 	 	 	 	 	 yield	 $line;
	 7:	 	 	 	 	 	 	 $line	 =	 fgets($f);
	 8:	 	 	 	 }                 読み込んだはしから値を返す
	 9:	 	 	 	 fclose($f); (ストリーム処理に最適)
10
11:	 }

             copyright(c) 2012 kuwata-lab.com all rights reserved.
リダイレクト v.s. パイプライン

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


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



          copyright(c) 2012 kuwata-lab.com all rights reserved.
リダイレクト v.s. パイプライン

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


bash% cat input | command1 
                | command2 
                | command3



          copyright(c) 2012 kuwata-lab.com all rights reserved.
ジェネレータの利点

◆ ループ処理から、汎用性の高い箇所だけを切り
  出せる(再利用性の向上)
◆ ひとつの大きなループを、複数の小さなループ
  に分解できる(ループの簡素化とPipeline化)
◆ メモリ消費量が少ない
  (巨大なデータを扱ってもプロセスが落ちない)
◆ データを読んだはしから処理できる
  (ストリームデータも処理可能)

        copyright(c) 2012 kuwata-lab.com all rights reserved.
どんな使い道があるの?
Advanced Generator




               copyright(c) 2012 kuwata-lab.com all rights reserved.
ジェネレータとインタラクション

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

             メインプログラム                                               ジェネレータ関数
                                                                    からメインプログ
                                                                    ラムに値を返す
   foreach(){}                                               yield	 $value
 $g->send($arg)

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



            copyright(c) 2012 kuwata-lab.com all rights reserved.
ジェネレータとインタラクション

双方向への値の受け渡しが可能に
メインプログラム

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

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

                                                      yield文の引数が
                                                      send()の戻り値に
ジェネレータ関数
     $arg	 =	 (yield	 "value");

           copyright(c) 2012 kuwata-lab.com all rights reserved.
ジェネレータとインタラクション

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

1$g	 =	 gfunc();                              function	 gfunc(){
$ret	 =	 $g->send(1);                         	 	 $arg	 =	 yield;
       2                                        3
$ret	 =	 $g->send(2);                         	 	 while	 (条件式)	 {
                                                 4
$ret	 =	 $g->send(3);                         	 	 	 	 $ret	 =	 ...;
                                                    5
                                              	 	 	 	 $arg	 =
                                              	 	 	 	 	 	 (yield	 $ret);
                                                        6
                                              	 	 	 	 var_dump($arg);
                                              	 	 }
                                              }

               copyright(c) 2012 kuwata-lab.com all rights reserved.
ジェネレータとインタラクション

メインプログラム                                   ジェネレータ関数
$g	 =	 gfunc();                             function	 gfunc(){
$ret	 =	 $g->send(1);                       	 	 $arg	 =	 yield;
$ret	 =	 $g->send(2);                       	 	 while	 (条件式)	 {
                                              9
        7
$ret	 =	 $g->send(3);                       	 	 	 	 $ret	 =	 ...;
                                                 10
                                            	 	 	 	 $arg	 =
                                            	 	 	 	 	 	 (yield	 $ret);
                                                     11
                                            	 	 	 	 var_dump($arg);
                                                  8
                                            	 	 }
                                            }

             copyright(c) 2012 kuwata-lab.com all rights reserved.
ジェネレータとインタラクション

メインプログラム                                   ジェネレータ関数
$g	 =	 gfunc();                             function	 gfunc(){
$ret	 =	 $g->send(1);                       	 	 $arg	 =	 yield;
$ret	 =	 $g->send(2);                       	 	 while	 (条件式)	 {
                                             14
$ret	 =	 $g->send(3);                       	 	 	 	 $ret	 =	 ...;
                                                 15
     12
                                            	 	 	 	 $arg	 =
                                            	 	 	 	 	 	 (yield	 $ret);
                                                     16
                                            	 	 	 	 var_dump($arg);
                                                 13
                                            	 	 }
                                            }

             copyright(c) 2012 kuwata-lab.com all rights reserved.
サンプル:数字あてゲーム
                            最初のsend()の引数値を変数に代入
1:	 function	 guess_quiz($num)	 {
2:	 	 	 	 $ans	 =	 yield;
3:	 	 	 	 while	 ($num	 !=	 $ans)	 {
4:	 	 	 	 	 	 	 if	 ($ans	 >	 $num)
5:	 	 	 	 	 	 	 	 	 	 $ans	 =	 (yield	 "too	 large");
6:	 	 	 	 	 	 	 else
7:	 	 	 	 	 	 	 	 	 	 $ans	 =	 (yield	 "too	 small");
8:	 	 	 	 }                     値を返し、かつsend()
9:	 }                           の引数値を変数に代入



                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文まで実行

               copyright(c) 2012 kuwata-lab.com all rights reserved.
ジェネレータとマルチスレッド

                                                                    Process
高機能



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

         Generator

                                                              リソース消費量
      : OSの機能として実現
      : 言語やライブラリで実現

            copyright(c) 2012 kuwata-lab.com all rights reserved.
ジェネレータと非同期処理

ネストしたコールバック関数
処理1(function($data)	 {
	 	 	 処理2(function($data)	 {
	 	 	 	 	 	 処理3(function($data)	 {
	 	 	 	 	 	 	 	 	 ....
	 	 	 	 	 	 });                                 読みにくい、
	 	 	 });                                       書きにくい、
});                                             わかりにくい




                    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..;                                                記述量が多い、
                                                                    書き方が不自然
});


            copyright(c) 2012 kuwata-lab.com all rights reserved.
ジェネレータと非同期処理

ジェネレータ
function	 doSomething()	 {
	 	 	 $data	 =	 yield;
	 	 	 ...処理1...;
	 	 	 $data	 =	 yield;
	 	 	 ...処理2...;
	 	 	 $data	 =	 yield;
	 	 	 ...処理3...;          自然な書き方で
                          わかりやすい!
}


           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 サーバのようなパーシステントプロセスのサーバを使って、
 リクエストを超えてジェネレータオブジェクトを保持できるような仕組みが必要。
               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:	 }
                               呼び出し側でbreakされると
                                終了処理が行われない!※
                            (しかもPHPにはfinallyがないorz)

※ SPLFileObject も同じ問題を抱えているが、デストラクタで fclose() している。
                   copyright(c) 2012 kuwata-lab.com all rights reserved.
落とし穴:リファクタリング

奇数番目をyieldしてから、偶数番目をyieldする
	 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:	 }
                                        DRYじゃない!関数化しよう!




              copyright(c) 2012 kuwata-lab.com all rights reserved.
落とし穴:リファクタリング

DRYになった、けど動かない!
	 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:	 }
              ジェネレータ関数を呼び出して
              いるがforeach文を使ってない

              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:	 }
              copyright(c) 2012 kuwata-lab.com all rights reserved.
まとめ

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




        copyright(c) 2012 kuwata-lab.com all rights reserved.
any questions?
おまけ
More Things




              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


           copyright(c) 2012 kuwata-lab.com all rights reserved.
おまけ:インデックスつきyield


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



               copyright(c) 2012 kuwata-lab.com all rights reserved.
おまけ:参照渡しでのyield


	 1:	 function	 &gfunc(&$arr)	 {                                        //	 実行結果
	 2:	 	 	 foreach	 ($arr	 as	 &$x){                                     array(3)	 {
	 3:	 	 	 	 	 yield	 $x;                                                	 	 [0]=>
	 4:	 	 	 }                                                             	 	 int(11)
	 5:	 }                                                                 	 	 [1]=>
	 6:	                                                                   	 	 int(21)
	 7:	 $arr	 =	 [10,	 20,	 30];                                          	 	 [2]=>
	 8:	 $g	 =	 gfunc($arr);                                               	 	 &int(31)
	 9:	 foreach	 ($g	 as	 &$x)                                            }
10:	 	 	 $x	 +=	 1;
11:	 var_dump($arr);


                copyright(c) 2012 kuwata-lab.com all rights reserved.
おまけ:クロージャとの比較

それ、クロージャでもできるよ!
function	 fib()	 {                              //	 使い方
	 	 list($x,	 $y)	 =                            $closure	 =	 fib();
	 	 	 	 [0,	 1];                                $x	 =	 $closure();
	 	 return	 function()                          while	 ($x	 <	 100)	 {
	 	 	 	 use	 ($x,	 $y)	 {                       	 	 echo	 $x,	 "n";
	 	 	 	 $tmp	 =	 $x;                            	 	 $x	 =	 $closure();
	 	 	 	 list($x,	 $y)	 =                        }
	 	 	 	 	 	 [$y,	 $x+$y];
	 	 	 	 return	 $tmp;
	 	 };
}


                 copyright(c) 2012 kuwata-lab.com all rights reserved.
おまけ:クロージャとの比較

クロージャ版                                       ジェネレータ版
function	 fib()	 {                            function	 fib()	 {
	 	 list($x,	 $y)	 =                          	 	 list($x,	 $y)	 =
	 	 	 	 [0,	 1];                              	 	 	 	 [0,	 1];
	 	 return	 function()                        	 	 while	 (TRUE)	 {
	 	 	 	 use	 ($x,	 $y)	 {                     	 	 	 	 yield	 $x;
	 	 	 	 $tmp	 =	 $x;                          	 	 	 	 list($x,	 $y)	 =
	 	 	 	 list($x,	 $y)	 =                      	 	 	 	 	 	 [$y,	 $x+$y];
	 	 	 	 	 	 [$y,	 $x+$y];                     	 	 }
	 	 	 	 return	 $tmp;                         }
	 	 };
        ・毎回先頭から実行される                                    ・前回の終了場所から自動
}          制約                                            的に再開 (より自然な記述)
    ・すべてをreturnの前に書                                     ・yieldの後ろにも処理が書
      かなければならない制約                                        ける (より自然な記述)
               copyright(c) 2012 kuwata-lab.com all rights reserved.
おまけ:内部イテレータとの比較

それ、内部イテレータでもできるよ!
function	 fib($fn)	 {                          //	 使い方
	 	 list($x,	 $y)	 =                           fib(function($x)	 {
	 	 	 	 [0,	 1];                               	 	 echo	 $x,	 "n";
	 	 while	 ($x	 <	 100)	 {                     });
	 	 	 	 $fn($x);
	 	 	 	 list($x,	 $y)	 =
	 	 	 	 	 	 [$y,	 $x+$y];
	 	 }
}




                copyright(c) 2012 kuwata-lab.com all rights reserved.
おまけ:内部イテレータとの比較

ループの終了条件も指定したい場合は、非常にブサイク
function	 fib($c,$fn){                        //	 使い方          終了条件と…
	 	 list($x,	 $y)	 =                          fib(
	 	 	 	 [0,	 1];                              	 	 function($x)	 {
	 	 while	 ($c($x))	 {                        	 	 	 	 return	 $x	 <	 100;
	 	 	 	 $fn($x);                              	 	 },
	 	 	 	 list($x,	 $y)	 =                      	 	 function($x)	 {
	 	 	 	 	 	 [$y,	 $x+$y];                     	 	 	 	 echo	 $x,	 "n";
	 	 }                                         	 	 }
}                                             );       ボディ部の両方が必要




               copyright(c) 2012 kuwata-lab.com all rights reserved.
おまけ:内部イテレータとの比較

可変箇所が複数ならジェネレータのほうがよっぽどきれい
function	 fib(){                                 //	 使い方
	 	 list($x,	 $y)	 =                             foreach(fib()	 as	 $x){
	 	 	 	 [0,	 1];                                 	 	 //	 終了条件
	 	 while	 (TRUE)	 {                             	 	 if	 ($x	 >=	 100)
	 	 	 	 yield	 $x;                               	 	 	 	 break;
	 	 	 	 list($x,	 $y)	 =                         	 	 //	 ボディ部
	 	 	 	 	 	 [$y,	 $x+$y];                        	 	 echo	 $x,	 "n";
	 	 }                                            };
}




                  copyright(c) 2012 kuwata-lab.com all rights reserved.
おまけ:内部イテレータとの比較
Rubyでは、1つの無名関数 (ブロック) で「終了条件」と
「ボディ部」の両方を指定できる。

def	 fib()                                        //	 使い方
	 	 x,	 y	 =	 0,	 1                               fib	 {|x|
	 	 while	 true                                   	 	 //	 終了条件
	 	 	 	 yield	 x                                  	 	 break	 if	 x	 >=	 100
	 	 	 	 x,	 y	 =	 y,	 x+y                         	 	 //	 ボディ部
	 	 end                                           	 	 puts	 x
end                                               }




                   copyright(c) 2012 kuwata-lab.com all rights reserved.
おまけ:「継続 (Continuation)」との比較

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




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

              copyright(c) 2012 kuwata-lab.com all rights reserved.
おまけ:ベンチマーク
                                                        Code: https://gist.github.com/3710544

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

          Array

    Generator

 Inner Iterator

       Closure
                  0                    0.2                       0.4                   0.6   0.8
                                                                                             (sec)



PHP: 5.5-addGeneratorSupport
OS: MacOSX
CPU: Core2DUO 2GHz
                               copyright(c) 2012 kuwata-lab.com all rights reserved.
おまけ:ベンチマーク
                                                        Code: https://gist.github.com/3710569



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

G + explode()

G + explode()
   + array()
                  0                    0.5                         1                   1.5    2
                                                                                             (sec)

                                              ループ内処理 (explode()やarray())
                                              のほうがよっぽど高コスト
PHP: 5.5-addGeneratorSupport
OS: MacOSX
CPU: Core2DUO 2GHz
                               copyright(c) 2012 kuwata-lab.com all rights reserved.
おまけ:参考文献

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

◆ Request for Comments: Generators
   https://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


                          copyright(c) 2012 kuwata-lab.com all rights reserved.
おしまい
1 of 63

Recommended

ADRという考えを取り入れてみて by
ADRという考えを取り入れてみてADRという考えを取り入れてみて
ADRという考えを取り入れてみてinfinite_loop
2.9K views49 slides
Swaggerでのapi開発よもやま話 by
Swaggerでのapi開発よもやま話Swaggerでのapi開発よもやま話
Swaggerでのapi開発よもやま話KEISUKE KONISHI
27.9K views36 slides
SQLアンチパターン~ファントムファイル by
SQLアンチパターン~ファントムファイルSQLアンチパターン~ファントムファイル
SQLアンチパターン~ファントムファイルItabashi Masayuki
9.9K views42 slides
PHP AST 徹底解説 by
PHP AST 徹底解説PHP AST 徹底解説
PHP AST 徹底解説do_aki
26.4K views77 slides
PHP と SAPI と ZendEngine3 と by
PHP と SAPI と ZendEngine3 とPHP と SAPI と ZendEngine3 と
PHP と SAPI と ZendEngine3 とdo_aki
16.1K views95 slides
アプリ開発で知っておきたい認証技術 - OAuth 1.0 + OAuth 2.0 + OpenID Connect - by
アプリ開発で知っておきたい認証技術 - OAuth 1.0 + OAuth 2.0 + OpenID Connect -アプリ開発で知っておきたい認証技術 - OAuth 1.0 + OAuth 2.0 + OpenID Connect -
アプリ開発で知っておきたい認証技術 - OAuth 1.0 + OAuth 2.0 + OpenID Connect -Naoki Nagazumi
22.8K views131 slides

More Related Content

What's hot

nginx入門 by
nginx入門nginx入門
nginx入門Takashi Takizawa
53.5K views73 slides
OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話 by
OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話
OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話Daichi Koike
2.5K views54 slides
PHP7で変わること ——言語仕様とエンジンの改善ポイント by
PHP7で変わること ——言語仕様とエンジンの改善ポイントPHP7で変わること ——言語仕様とエンジンの改善ポイント
PHP7で変わること ——言語仕様とエンジンの改善ポイントYoshio Hanawa
170.8K views51 slides
Nmap 9つの真実 by
Nmap 9つの真実Nmap 9つの真実
Nmap 9つの真実abend_cve_9999_0001
15.3K views90 slides
MQTTとAMQPと.NET by
MQTTとAMQPと.NETMQTTとAMQPと.NET
MQTTとAMQPと.NETterurou
39.8K views41 slides
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について by
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来についてshinjiigarashi
2.7K views59 slides

What's hot(20)

OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話 by Daichi Koike
OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話
OpenAPI 3.0でmicroserviceのAPI定義を試みてハマった話
Daichi Koike2.5K views
PHP7で変わること ——言語仕様とエンジンの改善ポイント by Yoshio Hanawa
PHP7で変わること ——言語仕様とエンジンの改善ポイントPHP7で変わること ——言語仕様とエンジンの改善ポイント
PHP7で変わること ——言語仕様とエンジンの改善ポイント
Yoshio Hanawa170.8K views
MQTTとAMQPと.NET by terurou
MQTTとAMQPと.NETMQTTとAMQPと.NET
MQTTとAMQPと.NET
terurou39.8K views
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について by shinjiigarashi
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
shinjiigarashi2.7K views
MySQL負荷分散の方法 by 佐久本正太
MySQL負荷分散の方法MySQL負荷分散の方法
MySQL負荷分散の方法
佐久本正太26.8K views
GoによるWebアプリ開発のキホン by Akihiko Horiuchi
GoによるWebアプリ開発のキホンGoによるWebアプリ開発のキホン
GoによるWebアプリ開発のキホン
Akihiko Horiuchi61K views
Redmineをちょっと便利に! プログラミング無しで使ってみるREST API by Go Maeda
Redmineをちょっと便利に! プログラミング無しで使ってみるREST APIRedmineをちょっと便利に! プログラミング無しで使ってみるREST API
Redmineをちょっと便利に! プログラミング無しで使ってみるREST API
Go Maeda32.3K views
実践 NestJS by Ayumi Goto
実践 NestJS実践 NestJS
実践 NestJS
Ayumi Goto1.2K views
Spring Boot × Vue.jsでSPAを作る by Go Miyasaka
Spring Boot × Vue.jsでSPAを作るSpring Boot × Vue.jsでSPAを作る
Spring Boot × Vue.jsでSPAを作る
Go Miyasaka14.9K views
OAuth2.0によるWeb APIの保護 by Naohiro Fujie
OAuth2.0によるWeb APIの保護OAuth2.0によるWeb APIの保護
OAuth2.0によるWeb APIの保護
Naohiro Fujie13.7K views
Where狙いのキー、order by狙いのキー by yoku0825
Where狙いのキー、order by狙いのキーWhere狙いのキー、order by狙いのキー
Where狙いのキー、order by狙いのキー
yoku082539.6K views
php-src の歩き方 by do_aki
php-src の歩き方php-src の歩き方
php-src の歩き方
do_aki2.4K views
GraphQLのsubscriptionで出来ること by Shingo Fukui
GraphQLのsubscriptionで出来ることGraphQLのsubscriptionで出来ること
GraphQLのsubscriptionで出来ること
Shingo Fukui9.1K views
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版) by Takuto Wada
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
Takuto Wada70.7K views
DBスキーマもバージョン管理したい! by kwatch
DBスキーマもバージョン管理したい!DBスキーマもバージョン管理したい!
DBスキーマもバージョン管理したい!
kwatch57.6K views
大規模ソーシャルゲーム開発から学んだPHP&MySQL実践テクニック by infinite_loop
大規模ソーシャルゲーム開発から学んだPHP&MySQL実践テクニック大規模ソーシャルゲーム開発から学んだPHP&MySQL実践テクニック
大規模ソーシャルゲーム開発から学んだPHP&MySQL実践テクニック
infinite_loop48.2K views

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

ちょっと詳しくJavaScript 第4回【スコープとクロージャ】 by
ちょっと詳しくJavaScript 第4回【スコープとクロージャ】ちょっと詳しくJavaScript 第4回【スコープとクロージャ】
ちょっと詳しくJavaScript 第4回【スコープとクロージャ】株式会社ランチェスター
1.5K views18 slides
Hack/HHVM 入門 by
Hack/HHVM 入門Hack/HHVM 入門
Hack/HHVM 入門y-uti
8.4K views41 slides
Clojure programming-chapter-2 by
Clojure programming-chapter-2Clojure programming-chapter-2
Clojure programming-chapter-2Masao Kato
1.1K views33 slides
test by
testtest
testa1yama1123
188 views7 slides
Perl 非同期プログラミング by
Perl 非同期プログラミングPerl 非同期プログラミング
Perl 非同期プログラミングlestrrat
7.3K views99 slides
最近の PHP の話 by
最近の PHP の話最近の PHP の話
最近の PHP の話y-uti
3.2K views32 slides

Similar to PHP5.5新機能「ジェネレータ」初心者入門(20)

Hack/HHVM 入門 by y-uti
Hack/HHVM 入門Hack/HHVM 入門
Hack/HHVM 入門
y-uti8.4K views
Clojure programming-chapter-2 by Masao Kato
Clojure programming-chapter-2Clojure programming-chapter-2
Clojure programming-chapter-2
Masao Kato1.1K views
Perl 非同期プログラミング by lestrrat
Perl 非同期プログラミングPerl 非同期プログラミング
Perl 非同期プログラミング
lestrrat7.3K views
最近の PHP の話 by y-uti
最近の PHP の話最近の PHP の話
最近の PHP の話
y-uti3.2K views
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~ by Akabane Hiroyuki
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~
Akabane Hiroyuki4.1K views
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in Tokyo by Tsuyoshi Yamamoto
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in TokyoGrails-1.1を斬る!~Grails-1.1からのチーム開発~ in Tokyo
Grails-1.1を斬る!~Grails-1.1からのチーム開発~ in Tokyo
Tsuyoshi Yamamoto931 views
OSC京都2011 by haganemetal
OSC京都2011OSC京都2011
OSC京都2011
haganemetal2.5K views
PHPの今とこれから2014 by Rui Hirokawa
PHPの今とこれから2014PHPの今とこれから2014
PHPの今とこれから2014
Rui Hirokawa21.3K views
今日からはじめるGPars by fumokmm
今日からはじめるGPars今日からはじめるGPars
今日からはじめるGPars
fumokmm4K views
PHPBLT#6 PHPの未来に入るかもしれない機能の紹介 by sters
PHPBLT#6 PHPの未来に入るかもしれない機能の紹介PHPBLT#6 PHPの未来に入るかもしれない機能の紹介
PHPBLT#6 PHPの未来に入るかもしれない機能の紹介
sters1.7K views
Node.js - JavaScript Thread Programming by takesako
Node.js - JavaScript Thread ProgrammingNode.js - JavaScript Thread Programming
Node.js - JavaScript Thread Programming
takesako1.9K views
速くなければスマフォじゃない - インターンバージョン- by Kazunari Hara
速くなければスマフォじゃない - インターンバージョン-速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-
Kazunari Hara962 views
PHPの今とこれから2019 by Rui Hirokawa
PHPの今とこれから2019PHPの今とこれから2019
PHPの今とこれから2019
Rui Hirokawa15.1K views
第三回ありえる社内勉強会 「いわががのLombok」 by yoshiaki iwanaga
第三回ありえる社内勉強会 「いわががのLombok」第三回ありえる社内勉強会 「いわががのLombok」
第三回ありえる社内勉強会 「いわががのLombok」
yoshiaki iwanaga14.1K views

More from kwatch

How to make the fastest Router in Python by
How to make the fastest Router in PythonHow to make the fastest Router in Python
How to make the fastest Router in Pythonkwatch
4.9K views66 slides
Migr8.rb チュートリアル by
Migr8.rb チュートリアルMigr8.rb チュートリアル
Migr8.rb チュートリアルkwatch
3.1K views64 slides
なんでもID by
なんでもIDなんでもID
なんでもIDkwatch
1.4K views42 slides
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方 by
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方kwatch
4.8K views20 slides
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方 by
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方kwatch
34K views58 slides
O/Rマッパーによるトラブルを未然に防ぐ by
O/Rマッパーによるトラブルを未然に防ぐO/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐkwatch
48.4K views61 slides

More from kwatch(20)

How to make the fastest Router in Python by kwatch
How to make the fastest Router in PythonHow to make the fastest Router in Python
How to make the fastest Router in Python
kwatch4.9K views
Migr8.rb チュートリアル by kwatch
Migr8.rb チュートリアルMigr8.rb チュートリアル
Migr8.rb チュートリアル
kwatch3.1K views
なんでもID by kwatch
なんでもIDなんでもID
なんでもID
kwatch1.4K views
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方 by kwatch
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方
Nippondanji氏に怒られても仕方ない、配列型とJSON型の使い方
kwatch4.8K views
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方 by kwatch
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
【SQLインジェクション対策】徳丸先生に怒られない、動的SQLの安全な組み立て方
kwatch34K views
O/Rマッパーによるトラブルを未然に防ぐ by kwatch
O/Rマッパーによるトラブルを未然に防ぐO/Rマッパーによるトラブルを未然に防ぐ
O/Rマッパーによるトラブルを未然に防ぐ
kwatch48.4K views
正規表現リテラルは本当に必要なのか? by kwatch
正規表現リテラルは本当に必要なのか?正規表現リテラルは本当に必要なのか?
正規表現リテラルは本当に必要なのか?
kwatch16.1K views
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5) by kwatch
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)
【公開終了】Python4PHPer - PHPユーザのためのPython入門 (Python2.5)
kwatch7.6K views
PHPとJavaScriptにおけるオブジェクト指向を比較する by kwatch
PHPとJavaScriptにおけるオブジェクト指向を比較するPHPとJavaScriptにおけるオブジェクト指向を比較する
PHPとJavaScriptにおけるオブジェクト指向を比較する
kwatch6.8K views
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か? by kwatch
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?
SQL上級者こそ知って欲しい、なぜO/Rマッパーが重要か?
kwatch59.8K views
Fantastic DSL in Python by kwatch
Fantastic DSL in PythonFantastic DSL in Python
Fantastic DSL in Python
kwatch13K views
What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策 by kwatch
What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策
What is wrong on Test::More? / Test::Moreが抱える問題点とその解決策
kwatch4.1K views
Pretty Good Branch Strategy for Git/Mercurial by kwatch
Pretty Good Branch Strategy for Git/MercurialPretty Good Branch Strategy for Git/Mercurial
Pretty Good Branch Strategy for Git/Mercurial
kwatch1.9K views
Oktest - a new style testing library for Python - by kwatch
Oktest - a new style testing library for Python -Oktest - a new style testing library for Python -
Oktest - a new style testing library for Python -
kwatch3.3K views
文字列結合のベンチマークをいろんな処理系でやってみた by kwatch
文字列結合のベンチマークをいろんな処理系でやってみた文字列結合のベンチマークをいろんな処理系でやってみた
文字列結合のベンチマークをいろんな処理系でやってみた
kwatch6.9K views
I have something to say about the buzz word "From Java to Ruby" by kwatch
I have something to say about the buzz word "From Java to Ruby"I have something to say about the buzz word "From Java to Ruby"
I have something to say about the buzz word "From Java to Ruby"
kwatch1.3K views
Cより速いRubyプログラム by kwatch
Cより速いRubyプログラムCより速いRubyプログラム
Cより速いRubyプログラム
kwatch5.3K views
Javaより速いLL用テンプレートエンジン by kwatch
Javaより速いLL用テンプレートエンジンJavaより速いLL用テンプレートエンジン
Javaより速いLL用テンプレートエンジン
kwatch4K views
Underlaying Technology of Modern O/R Mapper by kwatch
Underlaying Technology of Modern O/R MapperUnderlaying Technology of Modern O/R Mapper
Underlaying Technology of Modern O/R Mapper
kwatch3.7K views
How to Make Ruby CGI Script Faster - CGIを高速化する小手先テクニック - by kwatch
How to Make Ruby CGI Script Faster - CGIを高速化する小手先テクニック -How to Make Ruby CGI Script Faster - CGIを高速化する小手先テクニック -
How to Make Ruby CGI Script Faster - CGIを高速化する小手先テクニック -
kwatch6.9K views

Recently uploaded

速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料) by
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)NTT DATA Technology & Innovation
29 views38 slides
The Things Stack説明資料 by The Things Industries by
The Things Stack説明資料 by The Things IndustriesThe Things Stack説明資料 by The Things Industries
The Things Stack説明資料 by The Things IndustriesCRI Japan, Inc.
76 views29 slides
Windows 11 information that can be used at the development site by
Windows 11 information that can be used at the development siteWindows 11 information that can be used at the development site
Windows 11 information that can be used at the development siteAtomu Hidaka
90 views41 slides
今、改めて考えるPostgreSQLプラットフォーム - マルチクラウドとポータビリティ -(PostgreSQL Conference Japan 20... by
今、改めて考えるPostgreSQLプラットフォーム - マルチクラウドとポータビリティ -(PostgreSQL Conference Japan 20...今、改めて考えるPostgreSQLプラットフォーム - マルチクラウドとポータビリティ -(PostgreSQL Conference Japan 20...
今、改めて考えるPostgreSQLプラットフォーム - マルチクラウドとポータビリティ -(PostgreSQL Conference Japan 20...NTT DATA Technology & Innovation
151 views42 slides
光コラボは契約してはいけない by
光コラボは契約してはいけない光コラボは契約してはいけない
光コラボは契約してはいけないTakuya Matsunaga
25 views17 slides

Recently uploaded(12)

速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料) by NTT DATA Technology & Innovation
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)
The Things Stack説明資料 by The Things Industries by CRI Japan, Inc.
The Things Stack説明資料 by The Things IndustriesThe Things Stack説明資料 by The Things Industries
The Things Stack説明資料 by The Things Industries
CRI Japan, Inc.76 views
Windows 11 information that can be used at the development site by Atomu Hidaka
Windows 11 information that can be used at the development siteWindows 11 information that can be used at the development site
Windows 11 information that can be used at the development site
Atomu Hidaka90 views
今、改めて考えるPostgreSQLプラットフォーム - マルチクラウドとポータビリティ -(PostgreSQL Conference Japan 20... by NTT DATA Technology & Innovation
今、改めて考えるPostgreSQLプラットフォーム - マルチクラウドとポータビリティ -(PostgreSQL Conference Japan 20...今、改めて考えるPostgreSQLプラットフォーム - マルチクラウドとポータビリティ -(PostgreSQL Conference Japan 20...
今、改めて考えるPostgreSQLプラットフォーム - マルチクラウドとポータビリティ -(PostgreSQL Conference Japan 20...
光コラボは契約してはいけない by Takuya Matsunaga
光コラボは契約してはいけない光コラボは契約してはいけない
光コラボは契約してはいけない
Takuya Matsunaga25 views
PCCC23:東京大学情報基盤センター 「Society5.0の実現を目指す『計算・データ・学習』の融合による革新的スーパーコンピューティング」 by PC Cluster Consortium
PCCC23:東京大学情報基盤センター 「Society5.0の実現を目指す『計算・データ・学習』の融合による革新的スーパーコンピューティング」PCCC23:東京大学情報基盤センター 「Society5.0の実現を目指す『計算・データ・学習』の融合による革新的スーパーコンピューティング」
PCCC23:東京大学情報基盤センター 「Society5.0の実現を目指す『計算・データ・学習』の融合による革新的スーパーコンピューティング」
SSH応用編_20231129.pdf by icebreaker4
SSH応用編_20231129.pdfSSH応用編_20231129.pdf
SSH応用編_20231129.pdf
icebreaker4380 views
PCCC23:富士通株式会社 テーマ1「次世代高性能・省電力プロセッサ『FUJITSU-MONAKA』」 by PC Cluster Consortium
PCCC23:富士通株式会社 テーマ1「次世代高性能・省電力プロセッサ『FUJITSU-MONAKA』」PCCC23:富士通株式会社 テーマ1「次世代高性能・省電力プロセッサ『FUJITSU-MONAKA』」
PCCC23:富士通株式会社 テーマ1「次世代高性能・省電力プロセッサ『FUJITSU-MONAKA』」
SNMPセキュリティ超入門 by mkoda
SNMPセキュリティ超入門SNMPセキュリティ超入門
SNMPセキュリティ超入門
mkoda453 views

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

  • 1. PHPカンファレンス2012 PHP5.5新機能 かもしれない Generator 初心者入門 makoto kuwata <kwa@kuwata-lab.com> http://www.kuwata-lab.com/ 2012-09-15 (Sat) copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 2. 本発表について 【目的】 • PHP5.5の新機能かもしれない「ジェネレータ」を、 「なんだか凄そうだ」と思ってもらう。 【内容】 • ジェネレータって何? • どううれしいの? • どんなことに使えるの? 【注意】 • 内容は2012-09-15時点での情報に基づく。 今後、仕様変更があり得るので注意。 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 3. ジェネレータって何? What is Generator? copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 4. まとめ ◆ ジェネレータ セーブ機能 ◆ ジェネレータ関数 ゲームシナリオ ◆ ジェネレータオブジェクト 冒険の書 (セーブデータ) ◆ yield文 宿屋(セーブポイント) copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 5. 通常の関数 1: function func() { 2: $i = 0; 1, 2, 3回目 (0が返される) 3: return $i; 4: $i++; 5: return $i; 6: $i++; 7: return $i; 8: } 毎回先頭から実行され、また return文より後ろは実行されない copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 6. ジェネレータ (Generator) 関数 1: function gfunc() { 2: $i = 0; 1回目 (0が返される) 3: yield $i; 4: $i++; 5: yield $i; 2回目 (1が返される) 6: $i++; 7: yield $i; 3回目 (2が返される) 8: } 前回の終了位置から再開 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 7. 使い方 ジェネレータオブジェクトを生成 (通常の関数と使い方が違うことに注意!) 1: $g = gfunc(); 2: foreach ($g as $x) { 3: var_dump($x); 4: } foreach文とともに使用 (イテレータとして振る舞う) 実行例 int(0) int(1) int(2) copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 8. 実行順序:ループ1回目 メインプログラム ジェネレータ関数 1 $g = gfunc(); function gfunc(){ 2 foreach($g as $x){ $i = 0; 3 var_dump($x); yield $i; 4 5 } $i++; echo "donen"; yield $i; $i++; return $i; } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 9. 実行順序:ループ2回目 メインプログラム ジェネレータ関数 $g = gfunc(); function gfunc(){ 6 foreach($g as $x){ $i = 0; var_dump($x); yield $i; 9 } $i++; 7 echo "donen"; yield $i; 8 $i++; return $i; } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 10. 実行順序:ループ3回目…は、ない メインプログラム ジェネレータ関数 $g = gfunc(); function gfunc(){ 10foreach($g as $x){ $i = 0; var_dump($x); yield $i; } $i++; yield $i; 13echo "donen"; $i++; 11 return $i; 12 ・ループのたびにyield文まで実行 ・yield文の引数がループ変数に } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 11. サンプル: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: } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 12. サンプル:フィボナッチ数列 (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: } び出す側で指定する copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 13. まとめ ◆ ジェネレータ セーブ機能 ◆ ジェネレータ関数 ゲームシナリオ ◆ ジェネレータオブジェクト 冒険の書 (セーブデータ) ◆ yield文 宿屋(セーブポイント) copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 14. どううれしいの? Why Generator is so useful? copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 15. 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: copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 16. 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: copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 17. 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: copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 18. After: ジェネレータ関数 汎用性の高い箇所を関数に抽出 1: function each_line($filename) { 2: $f = fopen($filename, 'r'); 3: if ($f === FALSE) throw ....; 4: $line = fgets($f); 5: while ($line !== FALSE) { 6: $arr = explode("t", $line); yield $line; 7: echo $arr[1]; 汎用性の低い箇所を yield 文に 8: $line = fgets($f); 9: } 10: fclose($f); 11: } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 19. 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: } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 20. ジェネレータの利点 ◆ ループ処理から、汎用性の高い箇所だけを切り 出せる(再利用性の向上) ◆ ◆ ◆ copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 21. もっとジェネレータ関数 ジェネレータオブジェクトを受け取り、新し い別のジェネレータオブジェクトを生成する 1: // ジェネレータオブジェクトを作成 受け取る function each_fields($g) { 2: $g = each_line($filename); 3: // ループ 配列やイテレータでも可 4: foreach ($g as $line) { 5: $arr = explode("t", $line); 6: echo $arr[1]; yield $arr; 7: } 8: } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 22. ジェネレータを「重ねる」 1: // ジェネレータオブジェクトを生成 2: $g = each_line($filename); 3: $g = each_fields($g); ジェネレータから 4: // メインループ 別のジェネレータ 5: foreach ($g as $line) { を生成 $arr 6: $arr = explode("n", $line);                     7: echo $arr[1], "n"; 8: } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 23. 1つの大きなループ vs. 複数の小さなループ ジェネレータ使用前 ジェネレータ使用後 $f = fopen($filename, 'r'); while ($line !== FALSE) { $line = fgets($f); yield $line; while ($line !== FALSE) { } $arr = explode("n",$line); echo $arr[1]; foreach ($g as $line) { $line = fgets($f); yield $arr; } } fclose($f); $g = each_line($filename); $g = each_fields($g); foreach ($g as $arr) { echo $arr[1], "n"; } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 24. ジェネレータの利点 ◆ ループ処理から、汎用性の高い箇所だけを切り 出せる(再利用性の向上) ◆ ひとつの大きなループを、複数の小さなループ に分解できる(ループの簡素化とPipeline化) ◆ ◆ copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 25. 従来方法との比較:配列にすべて格納する 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: } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 26. 従来方法との比較:ジェネレータ 1: function each_line($filename) { 2: $f = fopen($filename, 'r'); 3: 1度に1行しか読み込まない 4: $line = fgets($f); (巨大なデータでも落ちない) 5: while ($line !== FALSE) { 6: yield $line; 7: $line = fgets($f); 8: } 読み込んだはしから値を返す 9: fclose($f); (ストリーム処理に最適) 10 11: } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 27. リダイレクト v.s. パイプライン すべてを配列に格納する ≒「リダイレクト」 ・巨大な中間ファイルが必要 ・最後まで処理しないと何も出力されない bash% command1 < input > tmp1 bash% command2 < tmp1 > tmp2 bash% command3 < tmp2 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 28. リダイレクト v.s. パイプライン ジェネレータを連結する ≒ 「パイプ」 ・巨大な中間ファイルがいらない ・読み込んだはしから出力される bash% cat input | command1 | command2 | command3 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 29. ジェネレータの利点 ◆ ループ処理から、汎用性の高い箇所だけを切り 出せる(再利用性の向上) ◆ ひとつの大きなループを、複数の小さなループ に分解できる(ループの簡素化とPipeline化) ◆ メモリ消費量が少ない (巨大なデータを扱ってもプロセスが落ちない) ◆ データを読んだはしから処理できる (ストリームデータも処理可能) copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 30. どんな使い道があるの? Advanced Generator copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 31. ジェネレータとインタラクション $g->send() … 次のyield文まで実行する メインプログラム ジェネレータ関数 からメインプログ ラムに値を返す foreach(){} yield $value $g->send($arg) メインプログラム ジェネレータ関数 からジェネレータ 関数に値を渡せる copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 32. ジェネレータとインタラクション 双方向への値の受け渡しが可能に メインプログラム $value = $g->send("arg"); send()の引数が yield文の値に yield文の引数が send()の戻り値に ジェネレータ関数 $arg = (yield "value"); copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 33. ジェネレータとインタラクション メインプログラム ジェネレータ関数 1$g = gfunc(); function gfunc(){ $ret = $g->send(1); $arg = yield; 2 3 $ret = $g->send(2); while (条件式) { 4 $ret = $g->send(3); $ret = ...; 5 $arg = (yield $ret); 6 var_dump($arg); } } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 34. ジェネレータとインタラクション メインプログラム ジェネレータ関数 $g = gfunc(); function gfunc(){ $ret = $g->send(1); $arg = yield; $ret = $g->send(2); while (条件式) { 9 7 $ret = $g->send(3); $ret = ...; 10 $arg = (yield $ret); 11 var_dump($arg); 8 } } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 35. ジェネレータとインタラクション メインプログラム ジェネレータ関数 $g = gfunc(); function gfunc(){ $ret = $g->send(1); $arg = yield; $ret = $g->send(2); while (条件式) { 14 $ret = $g->send(3); $ret = ...; 15 12 $arg = (yield $ret); 16 var_dump($arg); 13 } } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 36. サンプル:数字あてゲーム 最初のsend()の引数値を変数に代入 1: function guess_quiz($num) { 2: $ans = yield; 3: while ($num != $ans) { 4: if ($ans > $num) 5: $ans = (yield "too large"); 6: else 7: $ans = (yield "too small"); 8: } 値を返し、かつsend() 9: } の引数値を変数に代入 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 37. サンプル:数字あてゲーム 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文まで実行 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 38. ジェネレータとマルチスレッド Process 高機能 機能は限られるが Native Thread メモリ消費量が 極めて少ない (=大量生成可能) Green Thread Generator リソース消費量 : OSの機能として実現 : 言語やライブラリで実現 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 39. ジェネレータと非同期処理 ネストしたコールバック関数 処理1(function($data) { 処理2(function($data) { 処理3(function($data) { .... }); 読みにくい、 }); 書きにくい、 }); わかりにくい copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 40. ジェネレータと非同期処理 コールバック関数を数珠つなぎ $d = new Deferred(); $d->next(function($data) { ..処理1..; })->next(function($data) { ..処理2..; })->next(function($data) { ..処理3..; 記述量が多い、 書き方が不自然 }); copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 41. ジェネレータと非同期処理 ジェネレータ function doSomething() { $data = yield; ...処理1...; $data = yield; ...処理2...; $data = yield; ...処理3...; 自然な書き方で わかりやすい! } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 42. ジェネレータとページ遷移 1: $response = フォームを表示(); 2: $request = (yield $response); 3: $response = プレビューを表示($request); 4: $request = (yield $response); 5: データベースに登録($request); 複数ページにまたがる遷移を 非同期処理と同じように記述 ※ (詳しくは「継続ベース フレームワーク」でggr) ※ Apache だとリクエストごとにすべてをリセットするので、実現できない。 PHP のビルトイン Web サーバのようなパーシステントプロセスのサーバを使って、 リクエストを超えてジェネレータオブジェクトを保持できるような仕組みが必要。 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 43. 落とし穴: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: } 呼び出し側でbreakされると 終了処理が行われない!※ (しかもPHPにはfinallyがないorz) ※ SPLFileObject も同じ問題を抱えているが、デストラクタで fclose() している。 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 44. 落とし穴:リファクタリング 奇数番目をyieldしてから、偶数番目をyieldする 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: } DRYじゃない!関数化しよう! copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 45. 落とし穴:リファクタリング DRYになった、けど動かない! 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: } ジェネレータ関数を呼び出して いるがforeach文を使ってない copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 46. 落とし穴:リファクタリング 動くようになった…けどなんか腑に落ちない 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: } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 47. まとめ ◆ $->send()を使うと、双方向での値の受け渡し が可能 ◆ マルチスレッドよりも低機能だが軽量 ◆ 非同期処理が自然な形で記述できる ◆ 落とし穴もあるよ! copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 49. おまけ More Things copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 50. おまけ: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 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 51. おまけ:インデックスつきyield 1: function gfunc() { // 実行結果 2: yield 'a'; 0 => a 3: yield 'b'; 1 => b 4: yield 99=>'c'; 99 => c 5: } 6: 7: $g = gfunc(); 8: foreach ($g as $k=>$v) { 9: echo "$k => $v n"; 10: } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 52. おまけ:参照渡しでのyield 1: function &gfunc(&$arr) { // 実行結果 2: foreach ($arr as &$x){ array(3) { 3: yield $x; [0]=> 4: } int(11) 5: } [1]=> 6: int(21) 7: $arr = [10, 20, 30]; [2]=> 8: $g = gfunc($arr); &int(31) 9: foreach ($g as &$x) } 10: $x += 1; 11: var_dump($arr); copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 53. おまけ:クロージャとの比較 それ、クロージャでもできるよ! function fib() { // 使い方 list($x, $y) = $closure = fib(); [0, 1]; $x = $closure(); return function() while ($x < 100) { use ($x, $y) { echo $x, "n"; $tmp = $x; $x = $closure(); list($x, $y) = } [$y, $x+$y]; return $tmp; }; } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 54. おまけ:クロージャとの比較 クロージャ版 ジェネレータ版 function fib() { function fib() { list($x, $y) = list($x, $y) = [0, 1]; [0, 1]; return function() while (TRUE) { use ($x, $y) { yield $x; $tmp = $x; list($x, $y) = list($x, $y) = [$y, $x+$y]; [$y, $x+$y]; } return $tmp; } }; ・毎回先頭から実行される ・前回の終了場所から自動 } 制約 的に再開 (より自然な記述) ・すべてをreturnの前に書 ・yieldの後ろにも処理が書 かなければならない制約 ける (より自然な記述) copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 55. おまけ:内部イテレータとの比較 それ、内部イテレータでもできるよ! function fib($fn) { // 使い方 list($x, $y) = fib(function($x) { [0, 1]; echo $x, "n"; while ($x < 100) { }); $fn($x); list($x, $y) = [$y, $x+$y]; } } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 56. おまけ:内部イテレータとの比較 ループの終了条件も指定したい場合は、非常にブサイク function fib($c,$fn){ // 使い方 終了条件と… list($x, $y) = fib( [0, 1]; function($x) { while ($c($x)) { return $x < 100; $fn($x); }, list($x, $y) = function($x) { [$y, $x+$y]; echo $x, "n"; } } } ); ボディ部の両方が必要 copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 57. おまけ:内部イテレータとの比較 可変箇所が複数ならジェネレータのほうがよっぽどきれい function fib(){ // 使い方 list($x, $y) = foreach(fib() as $x){ [0, 1]; // 終了条件 while (TRUE) { if ($x >= 100) yield $x; break; list($x, $y) = // ボディ部 [$y, $x+$y]; echo $x, "n"; } }; } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 58. おまけ:内部イテレータとの比較 Rubyでは、1つの無名関数 (ブロック) で「終了条件」と 「ボディ部」の両方を指定できる。 def fib() // 使い方 x, y = 0, 1 fib {|x| while true // 終了条件 yield x break if x >= 100 x, y = y, x+y // ボディ部 end puts x end } copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 59. おまけ:「継続 (Continuation)」との比較 ◆ 継続のほうができることが広い、 ジェネレータはそのサブセット ※ ◆ 継続はcall stackを丸ごとコピーする ので重い、 ジェネレータはstack flame1つだけなので軽い ◆ 継続は理解するのがすーーーっごく難しい、 ジェネレータはわかりやすいし使いやすい ※処理系により実装方法は異なる場合がある copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 60. おまけ:ベンチマーク Code: https://gist.github.com/3710544 配列に詰め込む Loop のは高コスト Array Generator Inner Iterator Closure 0 0.2 0.4 0.6 0.8 (sec) PHP: 5.5-addGeneratorSupport OS: MacOSX CPU: Core2DUO 2GHz copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 61. おまけ:ベンチマーク Code: https://gist.github.com/3710569 Loop ジェネレータは 十分に低コスト Generator G + explode() G + explode() + array() 0 0.5 1 1.5 2 (sec) ループ内処理 (explode()やarray()) のほうがよっぽど高コスト PHP: 5.5-addGeneratorSupport OS: MacOSX CPU: Core2DUO 2GHz copyright(c) 2012 kuwata-lab.com all rights reserved.
  • 62. おまけ:参考文献 ◆ What PHP 5.5 might look like http://nikic.github.com/2012/07/10/What-PHP-5-5-might-look-like.html ◆ Request for Comments: Generators https://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 copyright(c) 2012 kuwata-lab.com all rights reserved.