以下の記事では、クラス(オブジェクト)の反復処理について 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"; } ?>
コメント