以下の記事では、クラス(オブジェクト)の反復処理について Iterator インターフェースを使うところまでは説明を行いました。
⇒PHP クラス(オブジェクト)の反復処理について
Iterator インターフェースでは5個のメソッドを宣言しなければならないので、そのあたりを簡単にできる方法を説明します。 比較の為に、前回のソースを以下に示します。
<?php
// Iterator インターフェースを利用した反復処理テストクラス
class cEach implements Iterator
{
// 内部の配列とする
private $list = ["list1", "list2", "list3", "list4", "list5"];
// 配列の位置
private $pos = 0;
// コンストラクタ
public function __construct() {
$this->pos = 0;
}
// 巻き戻し(初期位置)
public function rewind(): void {
$this->pos = 0;
}
// 現在値
public function current() {
return $this->list[$this->pos];
}
// 現在キー
public function key() {
return $this->pos;
}
// 次の位置へ
public function next(): void {
++$this->pos;
}
// 現在位置のデータ存在チェック
public function valid(): bool {
return isset($this->list[$this->pos]);
}
}
// [cEach]クラスの生成
$insEach = new cEach();
// インスタンスから取得できる反復データ
foreach($insEach as $key => $value) {
echo "$key => $value<br>\n";
}
?>
上記のクラスを今回は IteratorAggregate インターフェイスを使ってクラス(オブジェクト)の反復処理を書き変えます。
IteratorAggregate インターフェイスの概要は以下の様です。 getIterator メソッドの宣言は必須となります。
interface IteratorAggregate extends Traversable {
/* メソッド */
public getIterator(): Traversable // 外部イテレータを取得する
}
それでは、書き変えたソースを以下に示します。
getIterator メソッドの戻り値に ArrayIterator クラスの生成値を戻しています。
getIterator の戻り値は Traversable インターフェース型である必要がありますが、 ArrayIterator は配列型データを Traversable インターフェース型のクラスを生成しています。
<?php
// IteratorAggregate インターフェースを利用した反復処理テストクラス
class cEach implements IteratorAggregate
{
// 内部の配列とする
private $list;
// コンストラクタ
function __construct(array $_list) {
// 外部からの配列を内部に退避(配列はシャローコピーではないので)
$this->list = $_list;
}
// 外部イテレータを取得する
public function getIterator() {
return new ArrayIterator($this->list);
}
}
// [cEach]クラスの生成
$arr = ["list1", "list2", "list3", "list4", "list5"];
$insEach = new cEach($arr);
// インスタンスから取得できる反復データ
foreach($insEach as $key => $value) {
echo "$key => $value<br>\n";
}
?>
これを実行するとブラウザに以下の様に表示されます。
0 => list1 1 => list2 2 => list3 3 => list4 4 => list5
上記の ArrayIterator を使っている部分を ジェネレータ と呼ばれる yield を使う方法でもできます。
以下に変更したソースを示します。
<?php
// IteratorAggregate インターフェースを利用した反復処理テストクラス
class cEach implements IteratorAggregate
{
// 内部の配列とする
private $list;
// コンストラクタ
function __construct(array $_list) {
// 外部からの配列を内部に退避(配列はシャローコピーではないので)
$this->list = $_list;
}
// ジェネレータ(yield)を使用する
public function getIterator() {
foreach ($this->list as $key => $val) {
yield $key => $val;
}
}
}
// [cEach]クラスの生成
$arr = ["list1", "list2", "list3", "list4", "list5"];
$insEach = new cEach($arr);
// インスタンスから取得できる反復データ
foreach($insEach as $key => $value) {
echo "$key => $value<br>\n";
}
?>
ジェネレータ と呼ばれる yield について少し説明します。
PHP の公式ドキュメントには以下の様に記述されています。
ジェネレータ関数が呼ばれると、反復処理が可能なオブジェクトを返します。 このオブジェクトを (foreach ループなどで) 反復させると、値が必要になるたびに PHP がオブジェクトの反復メソッドを呼びます。 そして、ジェネレータが値を yield した時点の状態を保存しておき、 次に値が必要になったときにはそこから再開できるようにします。 yield できる値がなくなると、ジェネレータは単純に終了します。呼び出し元のコードでは、配列の要素をすべて処理し終えた後のように、そのまま処理が続きます。
ジェネレータ関数では yield は return 命令の様な感じで動きますが、 呼び出し元で繰り返し呼ばれることで、順次出力する内容が呼び出し元に返されます。
以下の例のジェネレータ関数は内部で宣言された配列を順次 yield で出力しています。 呼び出し元では foreach 命令の「$value」に値が返ってきます。
<?php
// ジェネレータ関数
function generator_func() {
$arr = ["list1", "list2", "list3"];
foreach($arr as $val) {
yield $val;
}
}
// ジェネレータ関数の呼出
foreach (generator_func() as $value) {
echo "$value<br>\n";
}
?>
yield では {キー} => {値} の形での指定もできます。ソースを変更しました。
<?php
// ジェネレータ関数
function generator_func() {
$arr = ["list1", "list2", "list3"];
foreach($arr as $key => $val) {
yield $key => $val;
}
}
// ジェネレータ関数の呼出
foreach (generator_func() as $key => $value) {
echo "$key => $value<br>\n";
}
?>
コメント