忍者ブログ

VB.NET-TIPS などプログラミングについて

VB.NETのTIPS(小技集)を中心に、Javascript、PHP その他のプログラミングについて少し役に立つ情報を発信します。いわゆる個人的な忘備録ですが、みなさんのお役に立てれば幸いです。

PHP クラス(オブジェクト)の反復処理について・その2(IteratorAggregate)


以下の記事では、クラス(オブジェクト)の反復処理について 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 できる値がなくなると、ジェネレータは単純に終了します。呼び出し元のコードでは、配列の要素をすべて処理し終えた後のように、そのまま処理が続きます。 


ジェネレータ関数では yieldreturn 命令の様な感じで動きますが、 呼び出し元で繰り返し呼ばれることで、順次出力する内容が呼び出し元に返されます。

以下の例のジェネレータ関数は内部で宣言された配列を順次 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";
}
?>











PR

コメント

コメントを書く