どのように機能し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
└ fileA
IteratorIterator
これは、ディレクトリツリーをたどる再帰を行わないものと簡単に比較できます。そして、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
ゲームが始まります。これは、通常のフラットなループで構造にドリルダウンするような方法で「再帰可能な」イテレータを呼び出す方法に関する知識を持っています。再帰的な動作を実行します。これは基本的に、イテレータの各値をステップオーバーし、再帰する「子」があるかどうかを調べ、それらの子のコレクションに出入りする作業を行います。のインスタンスをRecursiveIteratorIterator
foreach に挿入すると、構造にダイブするので、その必要はありません。
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
ます。
RecursiveIteraratorIterator
RecursiveIterator
利用可能な再帰動作を利用して、(「再帰可能」イテレータ)を反復するためのものです。
IteratorIterator
Iterator
非イテレータ、Traversable
オブジェクトに動作を適用するためのものです。
IteratorIterator
線形オーダートラバーサルの標準タイプだけではないTraversable
ですか?そのままでそのまま使えるものforeach
は?そしてさらに、ではないRecursiveIterator
、常にTraversable
そのためだけでなくIteratorIterator
だけでなく、RecursiveIteratorIterator
常に「適用するためのIterator
非イテレータは、Traversableのは、オブジェクトへの行動を」?(今度はforeach
、反復子タイプのインターフェースを実装するコンテナーオブジェクトに反復子オブジェクトを介して反復のタイプを適用するため、これらは常に反復子コンテナーオブジェクトですTraversable
)
IteratorIterator
はTraversable
オブジェクトをでラップすることに関するすべてのクラスですIterator
。もう何もない。あなたはより一般的な用語を適用しているようです。
Recursive
inは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