LoginSignup
10
9

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-09-24

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

ジェネレータ
(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"
}

*/
10
9
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
10
9