どのように機能しRecursiveIteratorIteratorますか?
PHPマニュアルには、文書化も説明もほとんどありません。違いは何であるIteratorIteratorとはRecursiveIteratorIterator?
どのように機能しRecursiveIteratorIteratorますか?
PHPマニュアルには、文書化も説明もほとんどありません。違いは何であるIteratorIteratorとはRecursiveIteratorIterator?
RecursiveIteratorIterator機能するか尋ねた場合、すでにどのようにIteratorIterator機能するか理解しましたか?つまり、基本的には同じですが、2つが使用するインターフェースのみが異なります。そして、あなたはいくつかの例にもっと興味がありますか、それとも基礎となるCコード実装の違いを見たいですか?
                IteratorIteratorマップIteratorとにIteratorAggregate入れられますIterator。ここでREcusiveIteratorIteratorは、recusivly aをトラバースするために使用されますRecursiveIterator
                回答:
RecursiveIteratorIterator具体的なIterator実装ツリー走査です。これにより、プログラマーはRecursiveIteratorインターフェースを実装するコンテナーオブジェクトをトラバースできます。イテレーターの一般的な原則、型、セマンティクス、およびパターンについては、ウィキペディアのイテレーターを参照してください。
線形順でのIteratorIterator具体的なIterator実装オブジェクトトラバーサルとは異なり(デフォルトTraversableではコンストラクターで任意の種類を受け入れます)、はオブジェクトの順序付けされたツリーRecursiveIteratorIterator内のすべてのノードをループし、コンストラクターはを受け取ります。RecursiveIterator
つまりRecursiveIteratorIterator、ツリーIteratorIteratorをループすることができ、リストをループすることができます。以下にいくつかのコード例を示します。
技術的には、これは、ノードのすべての子(存在する場合)をトラバースすることで線形性を破ることによって機能します。これは、定義によりノードのすべての子が再びであるために可能RecursiveIteratorです。次に、トップレベルIteratorはさまざまなRecursiveIteratorをその深さで内部的にスタックし、Iterator走査のために現在のアクティブなサブへのポインターを保持します。
これにより、ツリーのすべてのノードにアクセスできます。
基本的な原理はと同じIteratorIteratorです。インターフェースは反復のタイプを指定し、基本反復子クラスはこれらのセマンティクスの実装です。以下の例と比較してforeachください。新しいループを定義する必要がない限り(通常Iterator、具象型自体がを実装していない場合など)、線形ループでは実装の詳細についてあまり考慮しませんTraversable。
再帰的トラバーサルのために-あなたは事前に定義されて使用していない場合を除きTraversal、あなたは通常、 -すでに再帰的トラバーサル反復を持っていることを必要とする既存のインスタンス化するために、RecursiveIteratorIterator反復をかさえある再帰トラバーサル反復書き込みTraversableとトラバーサル反復のこのタイプを持っているあなた自身をforeach。
ヒント:おそらくどちらか一方を独自に実装しなかったので、これは、両者の違いを実際に体験するために行う価値があるかもしれません。答えの最後にDIYの提案があります。
要するに技術的な違い:
IteratorIteratorいずれかをとりTraversable、線形トラバーサルのために、RecursiveIteratorIteratorより具体的な必要RecursiveIteratorツリーをループにします。IteratorIteratorその主な公開するIteratorを経由してgetInnerIerator()、RecursiveIteratorIterator現在アクティブなサブを提供してIteratorのみ、そのメソッドを経由して。IteratorIterator、完全に親や子供のようなものを認識していない、RecursiveIteratorIteratorだけでなく子供を取得し、トラバースする方法を知っています。IteratorIteratorイテレータのスタックは必要ありません。RecursiveIteratorIteratorそのようなスタックがあり、アクティブなサブイテレータを認識しています。IteratorIterator起因する線形性と無選択にその秩序を持っている、RecursiveIteratorIterator(を経て決めた各ノードごとに決定するさらなるトラバーサルのための選択やニーズがあるごとにモードRecursiveIteratorIterator)。RecursiveIteratorIteratorより多くのメソッドがありIteratorIteratorます。要約するRecursiveIteratorと、は、それ自身のイテレータ、つまりで機能する具体的なタイプの反復(ツリーのループ)ですRecursiveIterator。これは、と同じ基本原理ですIteratorIeratorが、反復のタイプが異なります(線形順序)。
理想的には、独自のセットを作成することもできます。必要な唯一のものは、あること、あなたのイテレータ実装Traversableを介して可能ですIteratorかIteratorAggregate。その後、で使用できますforeach。たとえば、コンテナオブジェクトの対応する反復インターフェースと一緒に、ある種の三分木走査再帰反復オブジェクトを使用します。
それほど抽象的なものではない実際の例をいくつか見てみましょう。インターフェース、具体的なイテレータ、コンテナオブジェクト、反復のセマンティクスの間では、これは悪い考えではないかもしれません。
例としてディレクトリリストを取り上げます。次のファイルとディレクトリツリーがディスク上にあるとします。
線形順序のイテレータはトップレベルのフォルダとファイル(単一のディレクトリリスト)をトラバースするだけですが、再帰的なイテレータはサブフォルダもトラバースし、すべてのフォルダとファイル(サブディレクトリのリストを含むディレクトリリスト)をリストします。
Non-Recursive        Recursive
=============        =========
   [tree]            [tree]
    ├ dirA            ├ dirA
    └ fileA           │ ├ dirB
                      │ │ └ fileD
                      │ ├ fileB
                      │ └ fileC
                      └ fileAIteratorIteratorこれは、ディレクトリツリーをたどる再帰を行わないものと簡単に比較できます。そして、RecursiveIteratorIterator再帰リストが示すように、はツリーにトラバースできます。
最初は非常に基本的な例で、DirectoryIteratorそれを反復することTraversableができるforeachを実装しています:
$path = 'tree';
$dir  = new DirectoryIterator($path);
echo "[$path]\n";
foreach ($dir as $file) {
    echo " ├ $file\n";
}上記のディレクトリ構造の出力例は次のとおりです。
[tree]
 ├ .
 ├ ..
 ├ dirA
 ├ fileAご覧のとおり、これはIteratorIteratorまたはをまだ使用していませんRecursiveIteratorIterator。代わりにそれを使用するだけforeachで、Traversableインターフェースで動作します。
foreachのみ線形順序という名前の反復のタイプを知っているデフォルトでは、我々は、明示的に反復のタイプを指定したい場合があります。一見、冗長すぎるように見えるかもしれませんが、デモンストレーションの目的で(そしてRecursiveIteratorIterator後でわかりやすくするために)線形のタイプを指定しIteratorIteratorて、ディレクトリリストの反復のタイプを明示的に指定します。
$files = new IteratorIterator($dir);
echo "[$path]\n";
foreach ($files as $file) {
    echo " ├ $file\n";
}この例では、ほぼ最初のものと同じ、違いはある$files今IteratorIteratorのための反復のタイプTraversable $dir:
$files = new IteratorIterator($dir);いつものように、反復の行為はによって実行されますforeach:
foreach ($files as $file) {出力はまったく同じです。では何が違うのでしょうか?異なるのは、内で使用されるオブジェクトforeachです。最初の例ではDirectoryIterator、2番目の例ではIteratorIteratorです。これは、イテレータの柔軟性を示しています。それらを相互に置き換えることができ、内部のコードforeachは期待どおりに機能し続けます。
サブディレクトリを含むリスト全体を取得してみましょう。
これで反復のタイプを指定したので、それを別のタイプの反復に変更することを検討しましょう。
最初のレベルだけでなく、ツリー全体をトラバースする必要があることはわかっています。これをシンプルforeachで機能させるには、異なるタイプのイテレータが必要ですRecursiveIteratorIterator。そして、それはRecursiveIteratorインターフェースを持つコンテナオブジェクトに対してのみ繰り返すことができます。
インターフェースは契約です。それを実装するクラスは、とともに使用できますRecursiveIteratorIterator。そのようなクラスの例はRecursiveDirectoryIterator、の再帰的なバリアントのようなものですDirectoryIterator。
I-wordで他の文を書く前に、最初のコード例を見てみましょう:
$dir  = new RecursiveDirectoryIterator($path);
echo "[$path]\n";
foreach ($dir as $file) {
    echo " ├ $file\n";
}この3番目の例は、最初の例とほとんど同じですが、いくつかの異なる出力が作成されます。
[tree]
 ├ tree\.
 ├ tree\..
 ├ tree\dirA
 ├ tree\fileAさて、違いはありません。ファイル名の前にパス名が含まれていますが、残りも同様に見えます。
例に示されているように、ディレクトリオブジェクトはすでにRecursiveIteratorインターフェイスを実装していますが、foreachディレクトリツリー全体をトラバースするにはまだ十分ではありません。これがRecursiveIteratorIterator行動を起こすところです。例4は、その方法を示しています。
$files = new RecursiveIteratorIterator($dir);
echo "[$path]\n";
foreach ($files as $file) {
    echo " ├ $file\n";
}RecursiveIteratorIterator前の$dirオブジェクトだけでなくを使用foreachすると、すべてのファイルとディレクトリを再帰的に走査します。次に、オブジェクトの反復のタイプが指定されているため、すべてのファイルがリストされます。
[tree]
 ├ tree\.
 ├ tree\..
 ├ tree\dirA\.
 ├ tree\dirA\..
 ├ tree\dirA\dirB\.
 ├ tree\dirA\dirB\..
 ├ tree\dirA\dirB\fileD
 ├ tree\dirA\fileB
 ├ tree\dirA\fileC
 ├ tree\fileAこれは、フラットトラバーサルとツリートラバーサルの違いをすでに示しているはずです。RecursiveIteratorIterator要素のリストのような任意のツリー構造をトラバースすることができます。より多くの情報(反復が現在行われているレベルなど)があるため、反復処理中に反復子オブジェクトにアクセスして、たとえば出力をインデントすることができます。
echo "[$path]\n";
foreach ($files as $file) {
    $indent = str_repeat('   ', $files->getDepth());
    echo $indent, " ├ $file\n";
}例5の出力:
[tree]
 ├ tree\.
 ├ tree\..
    ├ tree\dirA\.
    ├ tree\dirA\..
       ├ tree\dirA\dirB\.
       ├ tree\dirA\dirB\..
       ├ tree\dirA\dirB\fileD
    ├ tree\dirA\fileB
    ├ tree\dirA\fileC
 ├ tree\fileA確かにこれはビューティーコンテストに勝つことはできませんが、再帰的な反復子を使用すると、キーと値の線形順序よりも多くの情報を利用できることがわかります。foreachこのような線形性しか表現できない場合でも、イテレータ自体にアクセスすると、より多くの情報を取得できます。
メタ情報と同様に、ツリーをトラバースして出力を順序付ける方法もいくつかあります。これはのモードでありRecursiveIteratorIterator、コンストラクターで設定できます。
次の例ではRecursiveDirectoryIterator、ドットエントリ(.と..)は不要なので削除するように指示します。ただし、再帰モードはSELF_FIRST、子(サブディレクトリ内のファイルとサブサブディレクトリ)の前に親要素(サブディレクトリ)を最初に()取るように変更されます。
$dir  = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
$files = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::SELF_FIRST);
echo "[$path]\n";
foreach ($files as $file) {
    $indent = str_repeat('   ', $files->getDepth());
    echo $indent, " ├ $file\n";
}出力には、適切にリストされたサブディレクトリエントリが表示されます。前の出力と比較すると、そこにはありませんでした。
[tree]
 ├ tree\dirA
    ├ tree\dirA\dirB
       ├ tree\dirA\dirB\fileD
    ├ tree\dirA\fileB
    ├ tree\dirA\fileC
 ├ tree\fileAしたがって、再帰モードは、ディレクトリの例として、ツリー内の分岐または葉が何をいつ返すかを制御します。
LEAVES_ONLY (デフォルト):ファイルのみをリストし、ディレクトリはリストしません。SELF_FIRST (上):リストディレクトリとそこにあるファイルCHILD_FIRST (例なし):最初にサブディレクトリ内のファイルをリストし、次にディレクトリをリストします。他の2つのモードでの例5の出力:
  LEAVES_ONLY                           CHILD_FIRST
  [tree]                                [tree]
         ├ tree\dirA\dirB\fileD                ├ tree\dirA\dirB\fileD
      ├ tree\dirA\fileB                     ├ tree\dirA\dirB
      ├ tree\dirA\fileC                     ├ tree\dirA\fileB
   ├ tree\fileA                             ├ tree\dirA\fileC
                                        ├ tree\dirA
                                        ├ tree\fileAそれを標準のトラバーサルと比較すると、これらすべてのものは利用できません。したがって、再帰的な反復は、頭を回す必要がある場合は少し複雑になりますが、イテレータと同じように動作するため、使いやすく、簡単に実行できますforeach。
これらは1つの答えの十分な例だと思います。完全なソースコードと見栄えの良いアスキーツリーを表示する例をこの要旨で見つけることができます:https : //gist.github.com/3599532
自分で行う:1
RecursiveTreeIterator行ずつ作業を行います。
例5は、利用可能なイテレータの状態に関するメタ情報があることを示しています。ただし、これはイテレーションの中で意図的に示されましたforeach。実際には、これは当然の内部に属しRecursiveIteratorます。
より良い例は、RecursiveTreeIteratorインデント、接頭辞などを処理します。次のコードを参照してください。
$dir   = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
$lines = new RecursiveTreeIterator($dir);
$unicodeTreePrefix($lines);
echo "[$path]\n", implode("\n", iterator_to_array($lines));RecursiveTreeIteratorラインによって作業ラインに意図され、出力はかなりまっすぐ進む一つの小さな問題です。
[tree]
 ├ tree\dirA
 │ ├ tree\dirA\dirB
 │ │ └ tree\dirA\dirB\fileD
 │ ├ tree\dirA\fileB
 │ └ tree\dirA\fileC
 └ tree\fileAと組み合わせて使用するとRecursiveDirectoryIterator、ファイル名だけでなくパス名全体が表示されます。残りはよさそうだ。これは、ファイル名がによって生成されるためSplFileInfoです。代わりにベース名として表示されます。必要な出力は次のとおりです。
/// Solved ///
[tree]
 ├ dirA
 │ ├ dirB
 │ │ └ fileD
 │ ├ fileB
 │ └ fileC
 └ fileAのRecursiveTreeIterator代わりに使用できるデコレータクラスを作成しますRecursiveDirectoryIterator。SplFileInfoパス名ではなく、現在のベース名を提供する必要があります。最終的なコードフラグメントは次のようになります。
$lines = new RecursiveTreeIterator(
    new DiyRecursiveDecorator($dir)
);
$unicodeTreePrefix($lines);
echo "[$path]\n", implode("\n", iterator_to_array($lines));を含むこれらのフラグメント$unicodeTreePrefixは、付録:自分で行う:RecursiveTreeIterator行ごとに作業を行うの要点の一部です。。
RecursiveIteratorIteratorこれは他のタイプと共通であるため、具体的な実装の詳細はまだ省略しましたが、実際にどのように機能するかについて、いくつかの技術情報を提供しました。私が思うの例はよく違いを示しています。反復のタイプがある 2の主な違い。反復のタイプを購入するかどうかは少し異なりますが、IMHOは反復のタイプのセマンティクスで容易ではありません。
                    違いは何ですか
IteratorIteratorとはRecursiveIteratorIterator?
これら2つのイテレーターの違いを理解するには、まず、使用される命名規則と、「再帰的」イテレーターの意味について少し理解する必要があります。
PHPには、などの非「再帰的」イテレータがArrayIteratorありFilesystemIteratorます。RecursiveArrayIteratorおよびなどの「再帰的」イテレータもありますRecursiveDirectoryIterator。後者にはそれらをドリルダウンできる方法がありますが、前者にはありません。
これらのイテレータのインスタンスが単独でループする場合、再帰的なものであっても、ネストされた配列またはサブディレクトリを持つディレクトリをループしても、値は「トップ」レベルからのみ取得されます。
再帰イテレータは、実装(経由再帰的な行動をhasChildren()、getChildren())ませんが悪用し、それを。
それは「recursible」イテレータとして再帰イテレータを考えた方が良いかもしれないが、彼らが持っている能力をを再帰的に反復することがなく、単に、これらのクラスの1つのインスタンスを反復処理すると、それを行うことはありません。再帰的な振る舞いを利用するには、読み続けてください。
ここでRecursiveIteratorIteratorゲームが始まります。これは、通常のフラットなループで構造にドリルダウンするような方法で「再帰可能な」イテレータを呼び出す方法に関する知識を持っています。再帰的な動作を実行します。これは基本的に、イテレータの各値をステップオーバーし、再帰する「子」があるかどうかを調べ、それらの子のコレクションに出入りする作業を行います。のインスタンスをRecursiveIteratorIteratorforeach に挿入すると、構造にダイブするので、その必要はありません。
RecursiveIteratorIteratorが使用されなかった場合は、独自の再帰ループを記述して、再帰的な動作を利用し、「再帰可能な」イテレータhasChildren()と照合してを使用する必要がありgetChildren()ます。
それがの簡単な概要ですがRecursiveIteratorIterator、それとどう違うのIteratorIteratorですか?ええと、あなたは基本的に、子猫と木の違いは何ですかと同じような質問をしています。両方が同じ百科事典(または反復子の場合はマニュアル)に表示されるからといって、2つの間で混乱する必要があるわけではありません。
の役割は、IteratorIterator任意のTraversableオブジェクトを取得し、Iteratorインターフェイスを満たすようにラップすることです。これを使用すると、非イテレータオブジェクトにイテレータ固有の動作を適用できます。
実際の例を示すと、DatePeriodクラスはですTraversableが、ではありませんIterator。そのため、その値をループすることはできますが、foreach()フィルタリングなど、通常イテレータで行う他のことはできません。
タスク:次の4週間の月曜日、水曜日、金曜日をループします。
はい、これはによって自明であるforeach上-ing DatePeriodと使用if()ループ内。しかし、それはこの例のポイントではありません!
$period = new DatePeriod(new DateTime, new DateInterval('P1D'), 28);
$dates  = new CallbackFilterIterator($period, function ($date) {
    return in_array($date->format('l'), array('Monday', 'Wednesday', 'Friday'));
});
foreach ($dates as $date) { … }上記のスニペットはCallbackFilterIterator、Iteratorインターフェイスを実装してDatePeriodいないクラスのインスタンスを予期しているため機能しません。ただし、Traversableなので、を使用することでその要件を簡単に満たすことができますIteratorIterator。
$period = new IteratorIterator(new DatePeriod(…));ご覧のとおり、これはイテレータクラスの反復や再帰とはまったく関係がなく、との違いがIteratorIteratorありRecursiveIteratorIteratorます。
RecursiveIteraratorIteratorRecursiveIterator利用可能な再帰動作を利用して、(「再帰可能」イテレータ)を反復するためのものです。
IteratorIteratorIterator非イテレータ、Traversableオブジェクトに動作を適用するためのものです。
IteratorIterator線形オーダートラバーサルの標準タイプだけではないTraversableですか?そのままでそのまま使えるものforeachは?そしてさらに、ではないRecursiveIterator 、常にTraversableそのためだけでなくIteratorIteratorだけでなく、RecursiveIteratorIterator常に「適用するためのIterator非イテレータは、Traversableのは、オブジェクトへの行動を」?(今度はforeach、反復子タイプのインターフェースを実装するコンテナーオブジェクトに反復子オブジェクトを介して反復のタイプを適用するため、これらは常に反復子コンテナーオブジェクトですTraversable)
                    IteratorIteratorはTraversableオブジェクトをでラップすることに関するすべてのクラスですIterator。もう何もない。あなたはより一般的な用語を適用しているようです。
                    RecursiveinはRecursiveIterator動作を意味するため、この名前は長い間私を混乱させましたが、より適切な名前は、などの機能を説明する名前RecursibleIteratorでした。
                    とともに使用するとiterator_to_array()、RecursiveIteratorIteratorすべての値を見つけるために配列を再帰的にウォークします。元の配列をフラット化することを意味します。
IteratorIterator 元の階層構造を維持します。
この例は、違いを明確に示しています。
$array = array(
               'ford',
               'model' => 'F150',
               'color' => 'blue', 
               'options' => array('radio' => 'satellite')
               );
$recursiveIterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
var_dump(iterator_to_array($recursiveIterator, true));
$iterator = new IteratorIterator(new ArrayIterator($array));
var_dump(iterator_to_array($iterator,true));new IteratorIterator(new ArrayIterator($array))はと同じです。new ArrayIterator($array)つまり、外側IteratorIteratorは何もしていません。さらに、出力のフラット化は何の関係もありませんiterator_to_array。イテレータを配列に変換するだけです。平坦化は、RecursiveArrayIteratorその内部反復子を歩く方法のプロパティです。
                    RecursiveDirectoryIteratorは、ファイル名だけでなく、パス名全体を表示します。残りはよさそうだ。これは、ファイル名がSplFileInfoによって生成されるためです。代わりにベース名として表示されます。必要な出力は次のとおりです。
$path =__DIR__;
$dir = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS);
$files = new RecursiveIteratorIterator($dir,RecursiveIteratorIterator::SELF_FIRST);
while ($files->valid()) {
    $file = $files->current();
    $filename = $file->getFilename();
    $deep = $files->getDepth();
    $indent = str_repeat('│ ', $deep);
    $files->next();
    $valid = $files->valid();
    if ($valid and ($files->getDepth() - 1 == $deep or $files->getDepth() == $deep)) {
        echo $indent, "├ $filename\n";
    } else {
        echo $indent, "└ $filename\n";
    }
}出力:
tree
 ├ dirA
 │ ├ dirB
 │ │ └ fileD
 │ ├ fileB
 │ └ fileC
 └ fileA