ORJavaScriptが有効ではありません。イテレーションイテレーションイテレータ/「Iterator」インターフェイス配列やオブジェクトのプロパティは、「foreach文」によって反復(イテレーション)して走査する事が出来ましたが、これは「foreach文」が“「コレクション」を走査する機能”を持っていたからです。「for文」や「while文」は同じ反復文であってもそのような機能を持たないため走査が出来ません。「コレクション」とは、配列やオブジェクト(のプロパティ)の様な、“要素の集まりを持つ構造体”の事です。“走査する機能”の具体的な内容は、配列の内部ポインタ(現在配列の何処を指しているかを示すもの)の移動や、ポインタが指す要素の有効性(ポインタが指す位置に要素があるか否か)のチェック等です。「foreach文」で配列を走査しようとすると、まずポインタが先頭要素を指すように初期化し、ループが開始されます。ループごとに現在ポインタが指す要素や添え字を返し、ポインタを1つ先の要素に移動し、移動先に要素があるか否かをチェック、無い(配列の要素数を超えた)場合にループから抜けます。これら全てを「foreach文」は自動的に実行してくれるのです。「for文」や「while文」で配列の走査を行う場合は、ポインタの移動を行う関数等を使用する必要があります。オブジェクトの場合はどうやって走査しているのか。それは、アクセス可能なプロパティへのリファレンスを何処かの配列に格納し、その配列に対して走査を行っているんです。配列やオブジェクトの走査は「foreach文」が持つ“走査する機能”によって行われますが、この“走査する機能”が機能するのは、配列やオブジェクトが“走査される能力”を持っているからです。この配列やオブジェクトが持つ“走査される能力”を「イテレータ」と呼びます。この「イテレータ」は「Iterator」インターフェイス(「foreach文」によって走査される「イテレータ」としての機能を備える)として定義されていて、この定義済みインターフェイスを(ちゃんと機能するように)実装して、自作の「イテレータ」を作る事が出来ます。「Iterator」インターフェイスを実装したクラスのオブジェクトを「foreach文」で走査しようとすると、そのオブジェクトが持つ「イテレータ」が実行されるようになります(各種メソッドが丁度“特殊メソッド”の様に自動的にコールされる)。「Iterator」インターフェイスを実装する際は、各種メソッドが「イテレータ」として機能するように処理部を実装しないと、正常に機能しません。以下のサンプルと結果をご覧下さい。******「Iterator」インターフェイスを実装した「MyIterator」クラス(インスタンスを生成しないので「抽象クラス」にした)を定義し、「プロパティ$arr」に、派生クラスのインスタンスが持つ全てのプロパティを配列の形で格納するようにしました。「MyIterator」クラスの派生クラス「HODE」クラスを定義し、コンストラクタ内の「$this->iterator_setting(get_object_vars($this));」によって全プロパティを連想配列にして「MyIterator」クラスの「プロパティ$arr」にセットします。「イテレータ」はこのプロパティ(配列)に対して走査を行います。「Iterator」インターフェイスを実装した「MyIterator」クラスを継承した「HODE」クラスは、「イテレータ」を自身の機能として備えるクラスになります(汗)。「Iterator」は下表に記した5つのメソッドを備えます。各メソッドは「イテレータ」として機能するように実装する必要があります。booleanサンプルの結果を見ると、「Iterator」インターフェイスを実装している方のオブジェクトでは各種メソッドが自動的にコールされているのが解かります。処理の流れを追うと、以下のようになっています。「メソッドrewind()」でポインタ初期化(最初に1回だけコールされる)。「メソッドvalid()」でポインタの有効性のチェック(1回目)。ポインタが有効でループ処理(1回目)。「メソッドcurrent()」でポインタが指す要素をゲット(1回目)。「メソッドkey()」でポインタが指す位置の添え字をゲット(1回目)。「メソッドnext()」でポインタを1つ先に移動(1回目)。「メソッドvalid()」(2回目)。ポインタが有効でループ処理(2回目)。「メソッドcurrent()」(2回目)。「メソッドkey()」(2回目)。「メソッドnext()」(2回目)。「メソッドvalid()」(3回目)。ポインタが有効でループ処理(3回目)。「メソッドcurrent()」(3回目)。「メソッドkey()」(3回目)。「メソッドnext()」(3回目)。「メソッドvalid()」(4回目)。ポインタが無効でループ終了。「イテレータ」のメソッドは、「foreach文」においては適宜自動的にコールされます。手動でコールすることも可能なので、「for文」や「while文」でも機能させることが出来ます。以下のサンプルをご覧下さい。}?></pre>結果は前回のサンプルと全く同じです。今回のサンプルでは、全てのメソッドを手動でコールしています。ついでに、以下のように実装すると、リファレンスを受け取れるようになります。---「プライベート」だろうが何だろうが書き換え可能です。ここまでのサンプルでは「HODE」クラスを作って、継承した「イテレータ」の機能で「HODE」クラスのオブジェクトのプロパティを走査してましたが、単にオブジェクトのプロパティを走査するサンプルとして作っただけのクラスなので、必ずしも必要ありません。「イテレータ」は自身の内部配列(「イテレータ」が持つプロパティに格納した配列)の走査時の振る舞いを定義したもので、「イテレータ」は単にこの内部配列を走査するだけなので、配列の形に形状を変えて内部配列に格納すれば、それを走査します。要するに、内部配列がセットされさえすれば、単独で使用することも出来ます。解説の順番間違えました(何)。以下のサンプルと結果をご覧下さい。******今回のサンプルでは、「MyIterator」クラスのコンストラクタ内で、渡された引数を型チェック後配列にして内部配列に格納しましたが、「MyIterator」クラスを継承して、どんな値が渡されるかを想定した各種用途別「イテレータ」を作る事も出来ます。集約クラス(コンポジション)/「IteratorAggregate」インターフェイスクラス定義において、「イテレータ」を自身に実装するか、もしくは「イテレータ」を実装したクラスを継承した場合は、そのクラス自身の機能として「イテレータ」を備える事になります。要するに、「Iterator」インターフェイスを実装するか、実装したクラスを継承したクラスは「イテレータ」になります。これとは違って、「イテレータ」を実装したクラスのオブジェクトをメンバに持ったりする(プロパティに「イテレータ」オブジェクトを格納するか、「イテレータ」オブジェクトを返す機能を持つ)クラスは、自身の機能としてではなく、メンバの機能として「イテレータ」を備える事になります。要するに、自身は「イテレータ」ではなく、「イテレータ」を持つクラスになります。クラス」の関係”と呼びます。要するに、“自身が「イテレータ」である”か、“自身は「イテレータ」を持っている(「イテレータ」は自身の一部)”という関係の違いです。“自身が何たらである”というクラスに対して、“自身は何たらを持っている”というクラス(別のクラスのオブジェクトをメンバにもつクラス)を特に「集約クラス(コンポジション)」と呼びます(と、思う・・・)。「集約クラス版イテレータ」は定義済みインターフェイス「IteratorAggregate」を実装して作成します。このインターフェイスを実装すると、「Iterator」インターフェイスを実装したクラスが“「イテレータ」である”と見なされたように、“「イテレータ」を持っている”と見なされるようになります。「IteratorAggregate」インターフェイスを実装したクラスのオブジェクトを「foreach文」で走査しようとすると、「イテレータ」オブジェクトを要求し、渡した「イテレータ」オブジェクトが持つ「イテレータ」が実行されます。これまでと違うのは、自身が持つ「イテレータ」機能が使われるのではなく、「イテレータ」機能を持つオブジェクトを渡し、そのオブジェクトが持つ「イテレータ」機能が使われる点です。以下のサンプルと結果をご覧下さい。------「Iterator」インターフェイスを実装した「MyIterator」クラスを定義。MyIterator($this->items);」によって、コンストラクタに渡された引数(配列)を格納した「プロパティ$items」を引数に指定して「MyIterator」クラスのオブジェクト(「イテレータ」オブジェクト)を生成し、「プロパティ$iterator」に格納します。「HODE」クラスには、「プロパティ$items」に要素を追加する「add()」メソッドと、配列構造をオブジェクト生成時の状態に戻す「reset()」メソッドを持たせました。「IteratorAggregate」は下表に記した1つのメソッドを備えます。Iteratorサンプルの結果を見ると、「IteratorAggregate」インターフェイスを実装している「HODE」クラスのオブジェクトを「foreach文」で走査しようとすると、まず「getIterator()」メソッドが自動的にコールされ、その後に「イテレータ」オブジェクトの「rewind()」メソッドがコールされているのが解かります。「getIterator()」メソッドで返された「イテレータ」オブジェクトの「イテレート」機能が使用されます。「Traversable」インターフェイスところで「インターフェイス」のページで、幾つかのインターフェイスが予め定義されているのを見ましたが、改めてその定義済みインターフェイスの名前とメソッド名を下記のスクリプトで見てみたいと思います。))現在定義されている7つのインターフェイスの内、「Traversable」と「IteratorAggregate」、「Iterator」、「RecursiveIterator」、「SeekableIterator」の5つが「イテレータ」に関係するインターフェイスです。これら5つのインターフェイスを実装してみます。以下のサンプルと結果をご覧下さい。))各「インターフェイス」を実装し、そのオブジェクトが実装する「インターフェイス」を調べました。「Traversable」インターフェイスを実装しようとすると以下の様なアラートと共に「E_ERROR」エラーを発します。“インターフェイス「Traversable」は「IteratorAggregate」か「Iterator」の一部として実装されなければならない”って事らしい。結果を見ると、全てのオブジェクトが「Traversable」インターフェイスを実装し、「RecursiveIterator」と「SeekableIterator」を実装したオブジェクトは「Iterator」インターフェイスも実装しているのが判ります。これらから、「イテレータ」に関係する5つの定義済みインターフェイスは、以下の様な継承関係にあると推測出来ます。SeekableIterator(シークできる「イテレータ」)そんなこんなで(どんなこんなで)、「Traversable」インターフェイス自体を実装したクラスを定義することは出来ません。「Traversable」は、「イテレータ」に関係するインターフェイスの頂点に君臨し、これらのインターフェイスを統括する為だけに存在します。(え?)、“犬”と“猫”は“動物”でも、“動物”が必ずしも“犬”か“猫”ではないように、「Traversable」が「イテレータ」というわけではありません。「Traversable」は名前が示す通り、“全探索できる”ものの頂点にあるもので、その下には「イテレータ」と関係ないものも存在します。以下のサンプルと結果をご覧下さい。}?></pre>TraversableSimpleXMLElement「SimpleXMLElement」クラスは「Traversable」インターフェイスを実装しますが、「イテレータ」とは無関係です。サンプル中の「ReflectionClass」クラスについては「リフレクション」のページで解説します。「RecursiveIterator」インターフェイス/「SeekableIterator」インターフェイス「Iterator」の派生インターフェイスである「RecursiveIterator」と「SeekableIterator」は、名前が示す通り、「RecursiveIterator」は“再帰的な「イテレータ」”で、「SeekableIterator」は“シークできる「イテレータ」”です。「RecursiveIterator」は「Iterator」に再帰処理を行うような機能を追加した「イテレータ」で、「SeekableIterator」は「Iterator」にシーク機能を追加した「イテレータ」です。「RecursiveIterator」と「SeekableIterator」は、「Iterator」が備えるメソッドの他にそれぞれ下表に記したメソッドを追加で備えます。void以下のサンプルと結果をご覧下さい。------まず、「Iterator」を実装した「MyIterator」クラスを定義。「SeekableIterator」を実装した「MySeekableIterator」クラスと、「RecursiveIterator」を実装した「MyRecursiveIterator」クラスを定義、何れも「MyIterator」クラスを継承します。各種「イテレータ」用に、「iteration()」関数、「s_iteration()」関数、「r_iteration()」関数を定義しました。$arr2」を定義し、これを引数に各種「イテレータ」オブジェクトを生成して、更にこれを引数に各種「イテレータ」用関数を実行しました。「s_iteration()」関数内では、「SeekableIterator」が備える「seek()」メソッドを使用していますが、このメソッドは、「指定された位置まで内部配列のポインタを移動する」機能を持っているはずのメソッドです。媒介用変数にセットされている添え字と要素がシーク前の値なので、「key()」メソッドと「current()」メソッドをコールして適切な値を得ます。「r_iteration()」関数内では、「hasChildren()」メソッド(「ポインタが指す位置の要素が配列であるか否かをチェックする」機能を持つ)をコールし、「TRUE」を返した時に「getChildren()」メソッド(「ポインタが指す位置の要素(配列)を引数に「リカーシブイテレータ」オブジェクトを返す」機能を持つ)をコールして、返って来た「リカーシブイテレータ」オブジェクトを引数に「r_iteration()」関数を再帰的にコール・・・を繰り返します。少々ややこしいですが、ここまでが「イテレータ」の基本です。定義済みの(実用的な)イテレータPHPには予め、幾つかの「イテレータ」が定義されています。通常はこの定義済みの「イテレータ」を使用します。自分で「イテレータ」を定義する必要はないのです。自分で定義することも出来ますが、定義済みのものを使った方がより安全で、正確で、何より楽です。Library(SPL)関数」として定義されています。「SPL関数」のより詳細な情報は以下のサイトで参照出来ます。Library(英語)と、いうわけで、ここでは定義済みの「イテレータ」にはどのようなものがあるのかだけ、下記のスクリプトを実行して見てみます。))「spl_classes()」関数は、「有効なSPLクラス名・インターフェイス名を配列に格納して返す」関数です。サンプルの結果を見ると、全て「イテレータ」に関係するクラス・インターフェイスである事が判ります。ついでに各SPLクラス・インターフェイスを分類して、更に備えるメソッド名を出力しました。サンプル中の「ReflectionClass」クラス等については「リフレクション」のページで解説します。最後に、「定義済みイテレータ」の中の「SimpleXMLIterator」の使い方だけ簡単に解説します。以下のサンプルと結果をご覧下さい。「iteration_xml.xml」という名前のXMLドキュメントを用意します。<description>PHP「&php;」入門者向けサイトです。<summary>入門講座です。<summary>関数のサンプルを交えたミニリファレンスです。</contents></site>このXMLドキュメントを下記のサンプルでロードします。Preprocessor」入門者向けサイトです。関数のサンプルを交えたミニリファレンスです。<description>PHP「&php;」入門者向けサイトです。<summary>入門講座です。<summary>関数のサンプルを交えたミニリファレンスです。</contents></site>使い方あってるか微妙ですが、こんな感じで。