PHP
generator
Iterator
yield

PHPのジェネレータを使った処理サンプル (yield構文)

More than 1 year has passed since last update.

※メモ程度のコードです。

ジェネレータ

(PHP 5 >= 5.5.0, PHP 7)


概略


  • PHP5.5から導入された


  • yieldreturn の代わりに記述することでループの中で都度値を返却できる


  • ある共通処理を関数化した際、配列Aを配列Bに代入してゴニョゴニョ・・など2つ配列を作ることでメモリを食ってしまうのをジェネレータ構文を使うことで改善できる(ことがある)


構文


generator.php


<?php

echo "-------basic_sample_1--------\n";

/*ベーシックなサンプル その1
1から3までの数値を出力する*/

function basic_sample_1 ()
{
$i = 0; //初回は 0
// yieldは終了ではなく保存ポイント

yield $i; //保存ポイント! 値を一旦戻す 0
//一回目ここまで ↑

//二回目ここから ↓
$i++; //2回目 プラス 1
yield $i; //保存ポイント! 値を一旦戻す 1
//二回目ここまで ↑

//三回目ここから ↓
$i++; //2回目 プラス 1
yield $i; //保存ポイント! 値を一旦戻す 2
//三回目ここまで ↑

}
// 前回の終了位置から、実行を再開できる

//ジェネレーターオブジェクトを生成する
$generator = basic_sample_1();

foreach ($generator as $x) {
// yield文の引数($i)がループ変数になる
var_dump($x);
}
/*
結果
int(0)
int(1)
int(2)
*/

echo "-------while_sample--------\n";

/* while文を用いたサンプル
1から10までの数値を順に出力する*/

function while_sample()
{
$i = 0;
//ジェネレータ側では終了条件は指定しない
while(true) {
$i++;
yield $i; //保存ポイント! 値を一旦戻す
}
}

$generator = while_sample();

foreach($generator as $x) {
var_dump($x);
//終了条件は呼び出し側で指定する
if ($x >= 10) {
break;
}
}
/*
結果
int(1)
int(2)
int(3)
int(4)
int(5)
int(6)
int(7)
int(8)
int(9)
int(10)
*/

echo "-------generator recursive--------\n";

/*
ジェネレータからジェネレータを呼ぶ例
CSVファイルに見立てた配列を一行ずつフィールドも分けた状態で配列に格納する
*/

$file = array(
'john,banana,water',
'mike,apple,milk',
'fredy,orange,coffee',
);

//1行ずつ読み込み
function each_record($file)
{
foreach ($file as $v) {
yield $v;
}
}

//1行を更にカンマで分ける
function each_field($generator)
{
foreach ($generator as $v) {
$field_array = explode(',', $v);
yield $field_array;
}
}

$generator = each_record($file);
$generator = each_field($generator);

foreach($generator as $v) {
$new_array[] = $v;
}

var_dump($new_array);

/*
結果
array(3) {
[0]=>
array(3) {
[0]=>
string(4) "john"
[1]=>
string(6) "banana"
[2]=>
string(5) "water"
}
[1]=>
array(3) {
[0]=>
string(4) "mike"
[1]=>
string(5) "apple"
[2]=>
string(4) "milk"
}
[2]=>
array(3) {
[0]=>
string(5) "fredy"
[1]=>
string(6) "orange"
[2]=>
string(6) "coffee"
}
}
*/

echo "-------merge using generator--------\n";

// この配列が巨大だった場合、単純にforeachで回すとメモリを食う
$arr1 = array(
array('hoge1' => 'fuga1'),
array('hoge2' => 'fuga2'),
array('hoge3' => 'fuga3'),
array('hoge4' => 'fuga4'),
);

$arr2 = array(
array('foo1' => 'bar1'),
array('foo2' => 'bar2'),
array('foo3' => 'bar3'),
array('foo4' => 'bar4'),
);

function arr1_parser($arr1)
{
foreach ($arr1 as $value) {
yield $value;
}
}

$generator = arr1_parser($arr1);

foreach ($generator as $key => $value) {
var_dump(array_merge($value, $arr2[$key]));
}

/*
結果
array(2) {
["hoge1"]=>
string(5) "fuga1"
["foo1"]=>
string(4) "bar1"
}
array(2) {
["hoge2"]=>
string(5) "fuga2"
["foo2"]=>
string(4) "bar2"
}
array(2) {
["hoge3"]=>
string(5) "fuga3"
["foo3"]=>
string(4) "bar3"
}
array(2) {
["hoge4"]=>
string(5) "fuga4"
["foo4"]=>
string(4) "bar4"
}

*/