Wie funktioniert RecursiveIteratorIterator in PHP?


88

Wie funktioniert das RecursiveIteratorIterator?

Das PHP-Handbuch hat nicht viel dokumentiert oder erklärt. Was ist der Unterschied zwischen IteratorIteratorund RecursiveIteratorIterator?


2
Es gibt ein Beispiel unter php.net/manual/en/recursiveiteratoriterator.construct.php und eine Einführung unter php.net/manual/en/class.iteratoriterator.php. Können Sie bitte darauf hinweisen, was genau Sie nicht verstehen können ? . Was sollte das Handbuch enthalten, um das Verständnis zu erleichtern?
Gordon

1
Wenn Sie fragen, wie es RecursiveIteratorIteratorfunktioniert, haben Sie bereits verstanden, wie es IteratorIteratorfunktioniert? Ich meine, es ist im Grunde das gleiche, nur die Schnittstelle, die von den beiden verwendet wird, ist unterschiedlich. Und interessieren Sie sich mehr für einige Beispiele oder möchten Sie den Unterschied der zugrunde liegenden C-Code-Implementierung sehen?
hakre

@ Gordon Ich war nicht sicher, wie eine einzelne foreach-Schleife alle Elemente in der Baumstruktur durchlaufen kann
varuog

@hakra Ich versuche jetzt, alle eingebauten Schnittstellen sowie die Implementierung von Spl-Schnittstellen und Iteratoren zu untersuchen. Ich war interessiert zu wissen, wie es im Hintergrund mit Forach-Schleife mit einigen Beispielen funktioniert.
Varuog

@hakre Sie sind beide tatsächlich sehr unterschiedlich. IteratorIteratorKarten Iteratorund IteratorAggregatein eine Iterator, wo REcusiveIteratorIteratorverwendet wird, um rekusiv aRecursiveIterator
Adam

Antworten:


247

RecursiveIteratorIteratorist eine konkrete Iteratorimplementierende Baumdurchquerung . Es ermöglicht einem Programmierer, ein Containerobjekt zu durchlaufen, das die RecursiveIteratorSchnittstelle implementiert. Die allgemeinen Prinzipien, Typen, Semantiken und Muster von Iteratoren finden Sie unter Iterator in Wikipedia .

Im Gegensatz dazu IteratorIteratorhandelt es sich um eine konkrete Iteratorimplementierende Objektdurchquerung in linearer Reihenfolge (und standardmäßig wird jede Art von Objekt Traversablein ihrem Konstruktor akzeptiert ). Das RecursiveIteratorIteratorerlaubt das Schleifen über alle Knoten in einem geordneten Baum von Objekten, und sein Konstruktor benötigt a RecursiveIterator.

Kurz gesagt: RecursiveIteratorIteratorErmöglicht das Durchlaufen eines Baums und IteratorIteratordas Durchlaufen einer Liste. Ich zeige das mit einigen Codebeispielen unten in Kürze.

Technisch funktioniert dies, indem die Linearität durch Durchlaufen aller untergeordneten Knoten eines Knotens (falls vorhanden) durchbrochen wird. Dies ist möglich, weil per Definition alle Kinder eines Knotens wieder a sind RecursiveIterator. Die oberste Ebene Iteratorstapelt dann intern die verschiedenen RecursiveIterators nach ihrer Tiefe und behält einen Zeiger auf das aktuell aktive Sub Iteratorfür die Durchquerung.

Dadurch können alle Knoten eines Baums besucht werden.

Die zugrunde liegenden Prinzipien sind dieselben wie bei IteratorIterator: Eine Schnittstelle gibt den Typ der Iteration an und die Basisiteratorklasse ist die Implementierung dieser Semantik. Vergleichen Sie mit den folgenden Beispielen, denn bei linearen Schleifen foreachdenken Sie normalerweise nicht viel über die Implementierungsdetails nach, es sei denn, Sie müssen eine neue definieren Iterator(z. B. wenn ein konkreter Typ selbst nicht implementiert wird Traversable).

Für rekursive Traversal - es sei denn , Sie nicht über einen vordefinierten verwenden , Traversaldass bereits rekursiven Traversal Iteration - Sie normalerweise benötigen die bestehende zu instanziiert RecursiveIteratorIteratorIteration oder sogar schreiben eine rekursive Traversal Iteration , die eine ist TraversableIhre eigene diese Art von Traversal Iteration mit haben foreach.

Tipp: Sie haben wahrscheinlich weder das eine noch das andere selbst implementiert. Dies ist möglicherweise eine wertvolle Maßnahme für Ihre praktische Erfahrung mit den Unterschieden, die sie aufweisen. Sie finden einen DIY-Vorschlag am Ende der Antwort.

Technische Unterschiede kurz:

  • Während IteratorIteratorjede dauert Traversablefür lineare Traversal, RecursiveIteratorIteratorbenötigt eine präziser RecursiveIteratorüber einen Baum - Schleife.
  • Wenn IteratorIteratordas Haupt- IteratorVia verfügbar gemacht wird getInnerIerator(), RecursiveIteratorIteratorwird das aktuell aktive Sub Iteratornur über diese Methode bereitgestellt.
  • Während er IteratorIteratorsich so etwas wie Eltern oder Kinder überhaupt nicht bewusst ist, RecursiveIteratorIteratorweiß er auch, wie man Kinder bekommt und durchquert.
  • IteratorIteratorbenötigt keinen Stapel von Iteratoren, RecursiveIteratorIteratorhat einen solchen Stapel und kennt den aktiven Unteriterator.
  • Wo IteratorIteratorhat seine Reihenfolge aufgrund der Linearität und keine Wahl, RecursiveIteratorIteratorhat eine Wahl für die weitere Durchquerung und muss pro Knoten entscheiden (entschieden über Modus proRecursiveIteratorIterator ).
  • RecursiveIteratorIteratorhat mehr Methoden als IteratorIterator.

Zusammenfassend: RecursiveIteratorist eine konkrete Art der Iteration (Schleife über einen Baum), die mit ihren eigenen Iteratoren arbeitet, nämlich RecursiveIterator. Das ist das gleiche Grundprinzip wie bei IteratorIerator, aber die Art der Iteration ist unterschiedlich (lineare Reihenfolge).

Idealerweise können Sie auch Ihr eigenes Set erstellen. Das einzige, was notwendig ist, ist, dass Ihr Iterator implementiert, Traversablewas über Iteratoroder möglich ist IteratorAggregate. Dann können Sie es mit verwenden foreach. Zum Beispiel eine Art rekursives Iterationsobjekt für die ternäre Baumdurchquerung zusammen mit der entsprechenden Iterationsschnittstelle für die Containerobjekte.


Lassen Sie uns einige Beispiele aus der Praxis betrachten, die nicht so abstrakt sind. Zwischen Schnittstellen, konkreten Iteratoren, Containerobjekten und Iterationssemantik ist dies möglicherweise keine so schlechte Idee.

Nehmen Sie als Beispiel eine Verzeichnisliste. Angenommen, Sie haben den folgenden Datei- und Verzeichnisbaum auf der Festplatte:

Verzeichnisbaum

Während ein Iterator mit linearer Reihenfolge nur den Ordner und die Dateien der obersten Ebene durchläuft (eine einzelne Verzeichnisliste), durchläuft der rekursive Iterator auch die Unterordner und listet alle Ordner und Dateien auf (eine Verzeichnisliste mit Listen seiner Unterverzeichnisse):

Non-Recursive        Recursive
=============        =========

   [tree]            [tree]
     dirA             dirA
     fileA             dirB
                         fileD
                        fileB
                        fileC
                       fileA

Sie können dies leicht vergleichen, mit IteratorIteratordem keine Rekursion zum Durchlaufen des Verzeichnisbaums erfolgt. Und das, RecursiveIteratorIteratorwas in den Baum gelangen kann, wie die rekursive Auflistung zeigt.

Zunächst ein sehr einfaches Beispiel mit einem DirectoryIterator, das implementiert, Traversabledas es ermöglicht, darüber foreachzu iterieren :

$path = 'tree';
$dir  = new DirectoryIterator($path);

echo "[$path]\n";
foreach ($dir as $file) {
    echo " ├ $file\n";
}

Die beispielhafte Ausgabe für die obige Verzeichnisstruktur lautet dann:

[tree]
  .
  ..
  dirA
  fileA

Wie Sie sehen, wird dies noch nicht verwendet IteratorIteratoroder RecursiveIteratorIterator. Stattdessen foreachfunktioniert nur die Verwendung auf der TraversableSchnittstelle.

Da foreachstandardmäßig nur der Iterationstyp mit dem Namen lineare Reihenfolge bekannt ist, möchten wir den Iterationstyp möglicherweise explizit angeben. Auf den ersten Blick mag es zu ausführlich erscheinen, aber zu Demonstrationszwecken (und um den Unterschied RecursiveIteratorIteratorspäter besser sichtbar zu machen ) können Sie den linearen Iterationstyp angeben, indem Sie den IteratorIteratorIterationstyp für die Verzeichnisliste explizit angeben :

$files = new IteratorIterator($dir);

echo "[$path]\n";
foreach ($files as $file) {
    echo " ├ $file\n";
}

Dieses Beispiel ist fast identisch mit dem ersten. Der Unterschied besteht darin, dass $fileses sich nun um eine IteratorIteratorArt Iteration handelt für Traversable $dir:

$files = new IteratorIterator($dir);

Wie üblich wird der Iterationsvorgang ausgeführt durch foreach:

foreach ($files as $file) {

Die Ausgabe ist genau gleich. Was ist also anders? Anders ist das Objekt, das in der foreach. Im ersten Beispiel ist es ein, DirectoryIteratorim zweiten Beispiel ist es das IteratorIterator. Dies zeigt die Flexibilität, die Iteratoren haben: Sie können sie durch andere ersetzen, der darin enthaltene Code foreachfunktioniert einfach wie erwartet weiter.

Beginnen wir mit dem Abrufen der gesamten Liste, einschließlich der Unterverzeichnisse.

Da wir jetzt den Iterationstyp angegeben haben, sollten Sie ihn in einen anderen Iterationstyp ändern.

Wir wissen, dass wir jetzt den ganzen Baum durchqueren müssen, nicht nur die erste Ebene. Damit dies mit einem einfachen funktioniert, foreachbenötigen wir einen anderen Iteratortyp:RecursiveIteratorIterator . Und das kann man nur über Containerobjekte iterieren, die die RecursiveIteratorSchnittstelle haben .

Die Schnittstelle ist ein Vertrag. Jede Klasse, die es implementiert, kann zusammen mit dem verwendet werdenRecursiveIteratorIterator . Ein Beispiel für eine solche Klasse ist die RecursiveDirectoryIterator, die so etwas wie die rekursive Variante von ist DirectoryIterator.

Sehen wir uns ein erstes Codebeispiel an, bevor wir einen anderen Satz mit dem I-Wort schreiben:

$dir  = new RecursiveDirectoryIterator($path);

echo "[$path]\n";
foreach ($dir as $file) {
    echo " ├ $file\n";
}

Dieses dritte Beispiel ist fast identisch mit dem ersten, erzeugt jedoch eine andere Ausgabe:

[tree]
  tree\.
  tree\..
  tree\dirA
  tree\fileA

Okay, nicht so anders, der Dateiname enthält jetzt den Pfadnamen vorne, aber der Rest sieht auch ähnlich aus.

Wie das Beispiel zeigt, implementiert selbst das Verzeichnisobjekt die RecursiveIteratorSchnittstelle bereits. Dies reicht noch nicht aus, um foreachden gesamten Verzeichnisbaum zu durchlaufen. Hier RecursiveIteratorIteratorkommt das zum Tragen. Beispiel 4 zeigt wie:

$files = new RecursiveIteratorIterator($dir);

echo "[$path]\n";
foreach ($files as $file) {
    echo " ├ $file\n";
}

Wenn Sie das Objekt RecursiveIteratorIteratoranstelle des vorherigen $dirObjekts verwenden, werden foreachalle Dateien und Verzeichnisse rekursiv durchlaufen. Dies listet dann alle Dateien auf, da der Typ der Objektiteration jetzt angegeben wurde:

[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

Dies sollte bereits den Unterschied zwischen Flach- und Baumdurchquerung zeigen. Der RecursiveIteratorIteratorkann jede baumartige Struktur als Liste von Elementen durchlaufen. Da es mehr Informationen gibt (wie die Ebene, auf der die Iteration gerade stattfindet), ist es möglich, auf das Iteratorobjekt zuzugreifen, während Sie es durchlaufen, und beispielsweise die Ausgabe einzurücken:

echo "[$path]\n";
foreach ($files as $file) {
    $indent = str_repeat('   ', $files->getDepth());
    echo $indent, " ├ $file\n";
}

Und Ausgabe von Beispiel 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

Sicher, dies gewinnt keinen Schönheitswettbewerb, aber es zeigt, dass mit dem rekursiven Iterator mehr Informationen verfügbar sind als nur die lineare Reihenfolge von Schlüssel und Wert . Selbst foreachwenn diese Art von Linearität nur ausgedrückt werden kann, können Sie durch Zugriff auf den Iterator selbst mehr Informationen erhalten.

Ähnlich wie bei den Metainformationen gibt es auch verschiedene Möglichkeiten, den Baum zu durchlaufen und daher die Ausgabe zu ordnen. Dies ist der Modus vonRecursiveIteratorIterator und kann mit dem Konstruktor festgelegt werden.

Das nächste Beispiel zeigt RecursiveDirectoryIteratoran, dass die Punkteinträge ( .und ..) entfernt werden sollen, da wir sie nicht benötigen. Aber auch der Rekursionsmodus wird geändert, um das übergeordnete Element (das Unterverzeichnis) zuerst ( SELF_FIRST) vor die untergeordneten Elemente (die Dateien und Unterverzeichnisse im Unterverzeichnis) zu setzen:

$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";
}

Die Ausgabe zeigt jetzt die ordnungsgemäß aufgelisteten Unterverzeichniseinträge, wenn Sie sie mit der vorherigen Ausgabe vergleichen, waren diese nicht vorhanden:

[tree]
  tree\dirA
     tree\dirA\dirB
        tree\dirA\dirB\fileD
     tree\dirA\fileB
     tree\dirA\fileC
  tree\fileA

Der Rekursionsmodus steuert daher, was und wann ein Brach oder Blatt im Baum zurückgegeben wird, für das Verzeichnisbeispiel:

  • LEAVES_ONLY (Standard): Nur Listendateien, keine Verzeichnisse.
  • SELF_FIRST (oben): Verzeichnis auflisten und dann die Dateien dort.
  • CHILD_FIRST (ohne Beispiel): Listet zuerst die Dateien im Unterverzeichnis und dann im Verzeichnis auf.

Ausgabe von Beispiel 5 mit den beiden anderen Modi:

  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

Wenn Sie das mit der Standarddurchquerung vergleichen, sind all diese Dinge nicht verfügbar. Rekursive Iteration ist daher etwas komplexer, wenn Sie Ihren Kopf darum wickeln müssen. Sie ist jedoch einfach zu verwenden, da sie sich wie ein Iterator verhältforeach und fertig.

Ich denke, das sind genug Beispiele für eine Antwort. Den vollständigen Quellcode sowie ein Beispiel für die Anzeige gut aussehender ASCII-Bäume finden Sie in dieser Übersicht: https://gist.github.com/3599532

Machen Sie es sich selbst: Machen Sie die RecursiveTreeIteratorArbeit Zeile für Zeile.

Beispiel 5 hat gezeigt, dass Metainformationen über den Status des Iterators verfügbar sind. Dies wurde jedoch in der foreachIteration gezielt demonstriert . Im wirklichen Leben gehört dies natürlich in die RecursiveIterator.

Ein besseres Beispiel ist das RecursiveTreeIterator, es kümmert sich um das Einrücken, Präfixieren und so weiter. Siehe folgendes Codefragment:

$dir   = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
$lines = new RecursiveTreeIterator($dir);
$unicodeTreePrefix($lines);
echo "[$path]\n", implode("\n", iterator_to_array($lines));

Das RecursiveTreeIteratorsoll Zeile für Zeile arbeiten, die Ausgabe ist ziemlich einfach mit einem kleinen Problem:

[tree]
  tree\dirA
   tree\dirA\dirB
    tree\dirA\dirB\fileD
   tree\dirA\fileB
   tree\dirA\fileC
  tree\fileA

In Kombination mit a RecursiveDirectoryIteratorwird der gesamte Pfadname und nicht nur der Dateiname angezeigt. Der Rest sieht gut aus. Dies liegt daran, dass die Dateinamen von generiert werden SplFileInfo. Diese sollten stattdessen als Basisname angezeigt werden. Die gewünschte Ausgabe ist die folgende:

/// Solved ///

[tree]
  dirA
   dirB
    fileD
   fileB
   fileC
  fileA

Erstellen Sie eine Dekorationsklasse, die RecursiveTreeIteratoranstelle von verwendet werden kann RecursiveDirectoryIterator. Es sollte den Basisnamen des aktuellen SplFileInfoanstelle des Pfadnamens enthalten. Das endgültige Codefragment könnte dann folgendermaßen aussehen:

$lines = new RecursiveTreeIterator(
    new DiyRecursiveDecorator($dir)
);
$unicodeTreePrefix($lines);
echo "[$path]\n", implode("\n", iterator_to_array($lines));

Diese Fragmente $unicodeTreePrefixsind Teil des Kerns im Anhang: Machen Sie es sich selbst: Machen Sie die RecursiveTreeIteratorArbeit Zeile für Zeile. .


Es beantwortet nicht die gestellten Fragen, enthält sachliche Fehler und verfehlt Kernpunkte, wenn Sie mit dem Anpassen der Iteration fortfahren. Alles in allem sieht es nach einem schlechten Versuch aus, die Prämie für ein Thema zu erhalten, über das Sie nicht viel wissen oder das Sie in diesem Fall nicht in eine Antwort auf die gestellten Fragen umwandeln können.
salathe

2
Nun, was meine "Warum" -Frage nicht beantwortet, Sie machen einfach mehr Worte, ohne viel zu sagen. Vielleicht fangen Sie tatsächlich mit einem Fehler an, der zählt? Zeigen Sie darauf, halten Sie es nicht geheim.
hakre

Der allererste Satz ist falsch: "RecursiveIteratorIterator ist ein IteratorIterator, der ... unterstützt", dies ist nicht wahr.
salathe

1
@salathe: Tank dich für dein Feedback. Ich habe die Antwort bearbeitet, um sie zu adressieren. Der erste Satz war in der Tat falsch und unvollständig. Ich habe die konkreten Implementierungsdetails immer noch weggelassen, RecursiveIteratorIteratorda dies mit anderen Typen gemeinsam ist, aber ich habe einige technische Informationen darüber gegeben, wie es tatsächlich funktioniert. Die Beispiele, die ich denke, zeigen gut die Unterschiede: Die Art der Iteration ist der Hauptunterschied zwischen den beiden. Keine Ahnung, wenn Sie die Art der Iteration kaufen, prägen Sie sie ein wenig anders, aber meiner Meinung nach ist es mit den semantischen Iterationstypen nicht einfach.
hakre

1
Der erste Teil ist etwas verbessert, aber sobald Sie sich den Beispielen zuwenden, werden immer noch sachliche Ungenauigkeiten festgestellt. Wenn Sie die Antwort nach der horizontalen Regel beschneiden, wird sie erheblich verbessert.
salathe

31

Was ist der Unterschied von IteratorIteratorund RecursiveIteratorIterator?

Um den Unterschied zwischen diesen beiden Iteratoren zu verstehen, muss man zunächst ein wenig über die verwendeten Namenskonventionen und die Bedeutung von "rekursiven" Iteratoren verstehen.

Rekursive und nicht rekursive Iteratoren

PHP hat nicht "rekursive" Iteratoren wie ArrayIteratorund FilesystemIterator. Es gibt auch "rekursive" Iteratoren wie RecursiveArrayIteratorundRecursiveDirectoryIterator . Letztere verfügen über Methoden, mit denen sie aufgeschlüsselt werden können, erstere nicht.

Wenn Instanzen dieser Iteratoren selbst durchlaufen werden, selbst die rekursiven, kommen die Werte nur von der "obersten" Ebene, selbst wenn sie ein verschachteltes Array oder Verzeichnis mit Unterverzeichnissen durchlaufen.

Die rekursiven Iteratoren implementieren rekursives Verhalten (via hasChildren(), getChildren()), nutzen es jedoch nicht aus.

Es könnte besser sein, sich die rekursiven Iteratoren als "rekursive" Iteratoren vorzustellen, sie haben die Fähigkeit rekursiv iteriert werden , sondern nur eine Instanz einer dieser Klassen iterieren wird das nicht tun. Lesen Sie weiter, um das rekursive Verhalten auszunutzen.

RecursiveIteratorIterator

Hier RecursiveIteratorIteratorkommt der ins Spiel. Es hat das Wissen, wie man die "rekursiven" Iteratoren so aufruft, dass sie in einer normalen, flachen Schleife in die Struktur eindringen. Es setzt das rekursive Verhalten in die Tat um. Es erledigt im Wesentlichen die Arbeit, über jeden der Werte im Iterator zu gehen, zu prüfen, ob es "Kinder" gibt, in die man zurückgreifen kann oder nicht, und in diese Sammlungen von Kindern hinein- und herauszugehen. Sie stecken eine Instanz von RecursiveIteratorIteratorin ein foreach und es taucht in die Struktur ein, so dass Sie nicht müssen.

Wenn das RecursiveIteratorIteratornicht verwendet wurde, müssten Sie Ihre eigenen rekursiven Schleifen schreiben, um das rekursive Verhalten auszunutzen, gegen die "rekursiven" Iteratoren zu prüfen hasChildren()und zu verwenden getChildren().

Das ist also ein kurzer Überblick darüber RecursiveIteratorIterator, wie es sich unterscheidet von IteratorIterator? Nun, Sie stellen im Grunde die gleiche Frage wie Was ist der Unterschied zwischen einem Kätzchen und einem Baum? Nur weil beide in derselben Enzyklopädie (oder im Handbuch für die Iteratoren) erscheinen, heißt das nicht, dass Sie zwischen den beiden verwechselt werden sollten.

IteratorIterator

Die Aufgabe von IteratorIteratorist es, ein beliebiges TraversableObjekt zu nehmen und es so zu verpacken, dass es der IteratorSchnittstelle entspricht. Eine Verwendung hierfür besteht darin, dann iterator-spezifisches Verhalten auf das Nicht-Iterator-Objekt anwenden zu können.

Um ein praktisches Beispiel zu geben, die DatePeriodKlasse ist Traversableaber keine Iterator. Als solches können wir seine Werte mit foreach()durchlaufen, aber keine anderen Dinge tun, die wir normalerweise mit einem Iterator tun würden, wie z. B. Filtern.

AUFGABE : Montags, mittwochs und freitags der nächsten vier Wochen.

Ja, dies ist trivial, indem Sie foreachüber DatePeriodund if()innerhalb der Schleife arbeiten. Aber darum geht es in diesem Beispiel nicht!

$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) {  }

Das obige Snippet funktioniert nicht, da es CallbackFilterIteratoreine Instanz einer Klasse erwartet, die die IteratorSchnittstelle implementiert , was DatePeriodjedoch nicht der Fall ist. Da dies jedoch der TraversableFall ist , können wir diese Anforderung leicht erfüllen, indem wir verwenden IteratorIterator.

$period = new IteratorIterator(new DatePeriod(…));

Wie Sie sehen können, hat dies überhaupt nichts mit Iteration über Iteratorklassen oder Rekursion zu tun, und darin liegt der Unterschied zwischen IteratorIteratorund RecursiveIteratorIterator.

Zusammenfassung

RecursiveIteraratorIteratordient zum Iterieren über einen RecursiveIterator("rekursiven" Iterator), wobei das verfügbare rekursive Verhalten ausgenutzt wird.

IteratorIteratordient zum Anwenden von IteratorVerhalten auf Nicht-Iterator- TraversableObjekte.


Ist das nicht IteratorIteratornur die Standardart der linearen Ordnungsdurchquerung für TraversableObjekte? Diejenigen, die ohne es verwendet werden könnten, so foreachwie es ist? Und noch weiter, ist nicht RecursiveIterator immer ein Traversableund daher nicht nurIteratorIterator sondern auch RecursiveIteratorIteratorimmer "zum Anwenden von IteratorVerhalten auf nicht iteratorische, durchquerbare Objekte" ? (Ich würde jetzt sagen, foreachwendet den Iterationstyp über das Iteratorobjekt auf Containerobjekte an, die eine Iterator-Typ-Schnittstelle implementieren, so dass dies immer Iterator-Container-Objekte sind. Traversable)
hakre

Wie meine Antwort besagt, IteratorIterator handelt es sich bei einer Klasse um das Umschließen von TraversableObjekten in eine Iterator. Nichts mehr . Sie scheinen den Begriff allgemeiner anzuwenden.
salathe

Scheinbar informative Antwort. Eine Frage: Würde RecursiveIteratorIterator nicht auch Objekte umbrechen, damit sie auch Zugriff auf das Iterator-Verhalten haben? Der einzige Unterschied zwischen den beiden wäre, dass der RecursiveIteratorIterator einen Drilldown durchführen kann, während der IteratorIterator dies nicht kann.
Mike Purcell

@salathe, wissen Sie, warum der rekursive Iterator (RecursiveDirectoryIterator) das Verhalten von hasChildren (), getChildren () nicht implementiert?
bis

7
+1 für "rekursiv". Der Name hat mich lange in die Irre geführt, weil dieRecursive in RecursiveIteratorVerhalten impliziert, während ein passenderer Name einer gewesen wäre, der Fähigkeiten beschreibt, wie RecursibleIterator.
Ziege

0

Wenn mit verwendet iterator_to_array(), RecursiveIteratorIteratorwird rekursiv die Array gehen alle Werte zu finden. Dies bedeutet, dass das ursprüngliche Array abgeflacht wird.

IteratorIterator behält die ursprüngliche hierarchische Struktur bei.

Dieses Beispiel zeigt Ihnen deutlich den Unterschied:

$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));

Das ist völlig irreführend. new IteratorIterator(new ArrayIterator($array))ist gleichbedeutend damit new ArrayIterator($array), dass das Äußere IteratorIteratornichts tut. Darüber hinaus hat die Abflachung der Ausgabe nichts damit zu tun iterator_to_array- sie konvertiert lediglich den Iterator in ein Array. Die Abflachung ist eine Eigenschaft des Weges RecursiveArrayIterator, den sein innerer Iterator geht.
Quolonel Fragen

0

RecursiveDirectoryIterator zeigt den gesamten Pfadnamen und nicht nur den Dateinamen an. Der Rest sieht gut aus. Dies liegt daran, dass die Dateinamen von SplFileInfo generiert werden. Diese sollten stattdessen als Basisname angezeigt werden. Die gewünschte Ausgabe ist die folgende:

$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";
    }
}

Ausgabe:

tree
  dirA
   dirB
    fileD
   fileB
   fileC
  fileA
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.