Jak działa RecursiveIteratorIterator w PHP?


88

Jak to RecursiveIteratorIteratordziała?

Podręcznik PHP nie ma nic dobrze udokumentowanego ani wyjaśnionego. Jaka jest różnica między IteratorIteratori RecursiveIteratorIterator?


2
jest przykład na php.net/manual/en/recursiveiteratoriterator.construct.php, a także wprowadzenie na php.net/manual/en/class.iteratoriterator.php - czy możesz wskazać, co dokładnie masz problem ze zrozumieniem . Co powinna zawierać instrukcja, aby była łatwiejsza do zrozumienia?
Gordon

1
Jeśli zapytasz, jak RecursiveIteratorIteratordziała, czy już zrozumiałeś, jak IteratorIteratordziała? Mam na myśli to, że jest w zasadzie taki sam, tylko interfejs, który jest używany przez te dwa, jest inny. Czy jesteś bardziej zainteresowany kilkoma przykładami, czy chcesz zobaczyć różnice między implementacją kodu C?
hakre

@Gordon Nie byłem pewien, w jaki sposób pojedyncza pętla foreach może przejść przez wszystkie elementy w strukturze drzewa
varuog

@hakra Próbuję teraz przestudiować wszystkie wbudowane interfejsy, a także interfejs spl i implementację iteratora. Byłem zainteresowany, aby wiedzieć, jak to działa w tle z pętlą forach z kilkoma przykładami.
varuog,

@hakre Oba są w rzeczywistości bardzo różne. IteratorIteratormapy Iteratori IteratorAggregatedo miejsca Iterator, gdzie REcusiveIteratorIteratorjest używany do przemierzania recusivly aRecursiveIterator
Adam

Odpowiedzi:


251

RecursiveIteratorIteratorjest konkretnym Iteratorwdrażaniu przechodzenie drzewa . Umożliwia programiście przechodzenie przez obiekt kontenera, który implementuje RecursiveIteratorinterfejs. Ogólne zasady, typy, semantyka i wzorce iteratorów można znaleźć w sekcji Iterator w Wikipedii .

W odróżnieniu od IteratorIteratorkonkretnej Iteratorimplementacji przechodzenia przez obiekt w porządku liniowym (i domyślnie akceptującej dowolny rodzaj Traversablew swoim konstruktorze), RecursiveIteratorIteratorumożliwia pętlę po wszystkich węzłach uporządkowanego drzewa obiektów, a jego konstruktor przyjmuje plik RecursiveIterator.

W skrócie: RecursiveIteratorIteratorpozwala na pętlę po drzewie,IteratorIterator pozwala na zapętlenie listy. Pokażę to wkrótce z kilkoma przykładami kodu poniżej.

Technicznie rzecz biorąc, działa to przez wyrwanie się z liniowości przez przejście przez wszystkie elementy potomne węzłów (jeśli takie istnieją). Jest to możliwe, ponieważ z definicji wszystkie dzieci węzła są ponownie RecursiveIterator. Górny poziom Iteratornastępnie wewnętrznie układa różne stosy RecursiveIteratorwedług ich głębokości i utrzymuje wskaźnik na bieżącym aktywnym podrzędnym Iteratorcelu przejścia.

Pozwala to na odwiedzenie wszystkich węzłów drzewa.

Podstawowe zasady są takie same jak w przypadku IteratorIterator: Interfejs określa typ iteracji, a podstawowa klasa iteratora jest implementacją tej semantyki. Porównaj z poniższymi przykładami, w przypadku pętli liniowej foreachzwykle nie myśl o szczegółach implementacji, chyba że musisz zdefiniować nowy Iterator(np. Gdy jakiś konkretny typ sam w sobie nie jestTraversable ).

W przypadku przemierzania rekurencyjnego - chyba że nie używasz predefiniowanej Traversaliteracji przechodzenia, która już ma rekurencyjną iterację przechodzenia - zwykle musisz utworzyć wystąpienie istniejącej RecursiveIteratorIteratoriteracji lub nawet napisać rekurencyjną iterację przechodzenia, która jest Traversabletwoją własną, aby mieć tego typu iterację przechodzenia foreach.

Wskazówka: prawdopodobnie nie zaimplementowałeś jednego ani drugiego własnego, więc może to być coś, co warto zrobić, aby poznać praktyczne różnice, jakie mają. Na końcu odpowiedzi znajdziesz sugestię zrób to sam.

Krótko mówiąc, różnice techniczne:

  • Chociaż IteratorIteratorpobiera dowolne Traversabledla przechodzenia liniowego, RecursiveIteratorIteratorpotrzebuje bardziej szczegółowej RecursiveIteratorpętli po drzewie.
  • Tam, gdzie IteratorIteratoreksponuje swoją główną Iteratorprzelotkę getInnerIerator(), RecursiveIteratorIteratorudostępnia bieżące aktywne sub- Iteratortylko za pomocą tej metody.
  • Chociaż IteratorIteratornie jest świadomy niczego takiego jak rodzic lub dzieci, RecursiveIteratorIteratorwie również, jak zdobyć i przemierzać dzieci.
  • IteratorIteratornie potrzebuje stosu iteratorów, RecursiveIteratorIteratorma taki stos i zna aktywny pod-iterator.
  • Gdzie IteratorIteratorma swoją kolejność ze względu na liniowość i nie ma wyboru, RecursiveIteratorIteratorma wybór dla dalszego przejścia i musi zdecydować dla każdego węzła (decyduje tryb naRecursiveIteratorIterator ).
  • RecursiveIteratorIteratorma więcej metod niż IteratorIterator.

Podsumowując: RecursiveIteratorto konkretny typ iteracji (pętla po drzewie), który działa na własnych iteratorach, a mianowicie RecursiveIterator. Jest to ta sama podstawowa zasada jak w przypadku IteratorIerator, ale typ iteracji jest inny (kolejność liniowa).

Idealnie byłoby również stworzyć własny zestaw. Jedyną konieczną rzeczą jest to, że Twój iterator implementuje, Traversableco jest możliwe za pośrednictwem Iteratorlub IteratorAggregate. Następnie możesz go używać z foreach. Na przykład jakiś obiekt iteracji rekurencyjnej przechodzenia przez drzewo trójskładnikowe wraz z odpowiednim interfejsem iteracji dla obiektów kontenera.


Przyjrzyjmy się kilku przykładom z życia, które nie są tak abstrakcyjne. Pomiędzy interfejsami, konkretnymi iteratorami, obiektami kontenerów i semantyką iteracji może nie jest to zły pomysł.

Weźmy jako przykład listę katalogów. Weź pod uwagę, że masz na dysku następujące drzewo plików i katalogów:

Drzewo katalogów

Podczas gdy iterator z porządkiem liniowym po prostu przechodzi przez folder i pliki najwyższego poziomu (lista pojedynczego katalogu), iterator rekurencyjny przechodzi również przez podfoldery i wyświetla listę wszystkich folderów i plików (lista katalogów z listą jego podkatalogów):

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

   [tree]            [tree]
    ├ dirA            ├ dirA
    └ fileA           │ ├ dirB
                      │ │ └ fileD
                      │ ├ fileB
                      │ └ fileC
                      └ fileA

Możesz łatwo porównać to, z IteratorIteratorktórym nie ma rekursji przy przechodzeniu po drzewie katalogów. I RecursiveIteratorIteratorktóry może przejść do drzewa, jak pokazuje lista Rekurencyjna.

Na początek bardzo prosty przykład z DirectoryIteratorimplementacją, Traversablektóra pozwala foreachna iterację :

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

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

Przykładowy wynik dla powyższej struktury katalogów to:

[tree]
 ├ .
 ├ ..
 ├ dirA
 ├ fileA

Jak widać, nie jest to jeszcze używane IteratorIteratorani RecursiveIteratorIterator. Zamiast tego po prostu używa foreachtego, który działa na Traversableinterfejsie.

Ponieważ foreachdomyślnie zna tylko typ iteracji o nazwie porządek liniowy, możemy chcieć jawnie określić typ iteracji. Na pierwszy rzut oka może się to wydawać zbyt rozwlekłe, ale dla celów demonstracyjnych (i aby różnica była RecursiveIteratorIteratorwidoczna później), określmy liniowy typ iteracji, wyraźnie określając IteratorIteratortyp iteracji dla listingu katalogów:

$files = new IteratorIterator($dir);

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

Ten przykład jest prawie identyczny z pierwszym, różnica polega na tym, że $filesjest to teraz IteratorIteratortyp iteracji dla Traversable $dir:

$files = new IteratorIterator($dir);

Jak zwykle czynność iteracji jest wykonywana przez foreach:

foreach ($files as $file) {

Wynik jest dokładnie taki sam. Więc co się zmieniło? Inny jest obiekt używany w foreach. W pierwszym przykładzie jest to DirectoryIteratoraw drugim przykładzie jest to IteratorIterator. Pokazuje to elastyczność, jaką mają iteratory: możesz je zastąpić innymi, kod wewnątrz foreachpo prostu nadal działa zgodnie z oczekiwaniami.

Zacznijmy od całej listy, łącznie z podkatalogami.

Ponieważ określiliśmy teraz typ iteracji, rozważmy zmianę go na inny typ iteracji.

Wiemy, że musimy teraz przejść całe drzewo, a nie tylko pierwszy poziom. Aby wykonać tę pracę z prostym foreach, potrzebujemy innego typu iteratora:RecursiveIteratorIterator . I to można tylko iterować po obiektach kontenerów, które mają RecursiveIteratorinterfejs .

Interfejs jest umową. Każda klasa implementująca ją może być używana razem z RecursiveIteratorIterator. Przykładem takiej klasy jestRecursiveDirectoryIterator , która jest czymś w rodzaju rekurencyjnej odmiany DirectoryIterator.

Zobaczmy pierwszy przykład kodu przed napisaniem jakiegokolwiek innego zdania ze słowem I:

$dir  = new RecursiveDirectoryIterator($path);

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

Ten trzeci przykład jest prawie identyczny z pierwszym, ale tworzy inne wyniki:

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

Okej, nie tak inaczej, nazwa pliku zawiera teraz nazwę ścieżki z przodu, ale reszta również wygląda podobnie.

Jak pokazuje przykład, nawet obiekt katalogu już implementuje RecursiveIteratorinterfejs, ale to nie wystarczy, aby foreachprzejść przez całe drzewo katalogów. Tutaj RecursiveIteratorIteratorzaczyna się akcja. Przykład 4 pokazuje, jak:

$files = new RecursiveIteratorIterator($dir);

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

Użycie RecursiveIteratorIteratorzamiast tylko poprzedniego $dirobiektu spowoduje foreachprzechodzenie przez wszystkie pliki i katalogi w sposób rekurencyjny. Następnie wyświetla listę wszystkich plików, ponieważ typ iteracji obiektu został określony teraz:

[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

Powinno to już pokazać różnicę między przechodzeniem płaskim i przechodzeniem po drzewie. RecursiveIteratorIteratorJest w stanie przechodzić żadnego drzewiastą strukturę w postaci listy elementów. Ponieważ jest więcej informacji (takich jak poziom, na którym obecnie odbywa się iteracja), można uzyskać dostęp do obiektu iteratora podczas iteracji po nim i na przykład wcinać dane wyjściowe:

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

Wynik przykładu 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

Oczywiście to nie wygrywa konkursu piękności, ale pokazuje, że w przypadku iteratora rekurencyjnego dostępnych jest więcej informacji niż tylko liniowy porządek klucza i wartości . Nawet foreachmoże wyrazić ten rodzaj liniowości, dostęp do samego iteratora pozwala uzyskać więcej informacji.

Podobnie jak w przypadku metainformacji, istnieją również różne sposoby poruszania się po drzewie, a tym samym porządkowania wyników. To jest tryb programuRecursiveIteratorIterator i można go ustawić za pomocą konstruktora.

W następnym przykładzie RecursiveDirectoryIteratorpolecenie usunie kropki ( .i ..), ponieważ ich nie potrzebujemy. Ale także tryb rekursji zostanie zmieniony, aby najpierw zająć element nadrzędny (podkatalog) (SELF_FIRST ) przed dziećmi (pliki i podkatalogi w podkatalogu):

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

Dane wyjściowe pokazują teraz poprawnie wymienione pozycje podkatalogów, jeśli porównasz z poprzednim wyjściem, których tam nie było:

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

Dlatego tryb rekurencyjny kontroluje, co i kiedy zwracane jest ramię lub liść w drzewie, dla przykładu katalogu:

  • LEAVES_ONLY (domyślnie): Tylko lista plików, bez katalogów.
  • SELF_FIRST (powyżej): Lista katalogu, a następnie plików w nim.
  • CHILD_FIRST (bez przykładu): Najpierw wyświetla listę plików w podkatalogu, a następnie w katalogu.

Dane wyjściowe przykładu 5 z dwoma innymi trybami:

  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

Porównując to ze standardowym przechodzeniem, wszystkie te rzeczy nie są dostępne. Dlatego iteracja rekurencyjna jest nieco bardziej złożona, gdy trzeba ją owinąć, jednak jest łatwa w użyciu, ponieważ zachowuje się jak iterator, umieszcza się ją w a foreachi gotowe.

Myślę, że to wystarczające przykłady na jedną odpowiedź. Możesz znaleźć pełny kod źródłowy, a także przykład, aby wyświetlić ładnie wyglądające drzewa ascii w tym skrócie: https://gist.github.com/3599532

Zrób to sam: stwórz RecursiveTreeIteratorpracę wiersz po wierszu.

Przykład 5 wykazał, że są dostępne metainformacje o stanie iteratora. Jednak zostało to celowo zademonstrowane w ramach foreachiteracji. W prawdziwym życiu to naturalnie należy doRecursiveIterator .

Lepszym przykładem jest RecursiveTreeIterator, zajmuje się wcięciami, przedrostkami i tak dalej. Zobacz następujący fragment kodu:

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

RecursiveTreeIteratorPrzeznaczony jest do pracy przy linii linii, wyjście jest dość proste z jednym małym problemem:

[tree]
 ├ tree\dirA
 │ ├ tree\dirA\dirB
 │ │ └ tree\dirA\dirB\fileD
 │ ├ tree\dirA\fileB
 │ └ tree\dirA\fileC
 └ tree\fileA

W połączeniu z a RecursiveDirectoryIteratorwyświetla całą nazwę ścieżki, a nie tylko nazwę pliku. Reszta wygląda dobrze. Dzieje się tak, ponieważ nazwy plików są generowane przez SplFileInfo. Zamiast tego powinny być wyświetlane jako nazwa basenowa. Żądane wyjście jest następujące:

/// Solved ///

[tree]
 ├ dirA
 │ ├ dirB
 │ │ └ fileD
 │ ├ fileB
 │ └ fileC
 └ fileA

Utwórz klasę dekoratora, której można używać RecursiveTreeIteratorzamiast RecursiveDirectoryIterator. Powinien zawierać nazwę podstawową prądu SplFileInfozamiast ścieżki. Ostateczny fragment kodu mógłby wtedy wyglądać następująco:

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

Te fragmenty, w tym, $unicodeTreePrefixsą częścią istoty w Dodatku: Zrób to sam: Stwórz RecursiveTreeIteratorpracę wiersz po wierszu. .


Nie odpowiada na zadane pytania, zawiera błędy rzeczowe i pomija kluczowe punkty, gdy przechodzisz do dostosowywania iteracji. Podsumowując, wygląda to na kiepską próbę zdobycia nagrody za temat, o którym niewiele wiesz lub, jeśli tak, nie możesz udzielić odpowiedzi na postawione pytania.
salathe

2
Cóż, co nie odpowiada na moje pytanie „dlaczego”, po prostu piszesz więcej słów, nie mówiąc zbyt wiele. Może zaczynasz od błędu, który się liczy? Wskaż to, nie ukrywaj tego.
hakre

Pierwsze zdanie jest niepoprawne: „RecursiveIteratorIterator to IteratorIterator obsługujący…”, to nieprawda.
salathe

1
@salathe: Dziękuję za opinie. Zredagowałem odpowiedź, aby ją adresować. Pierwsze zdanie rzeczywiście było błędne i niekompletne. Nadal pominąłem konkretne szczegóły implementacji, RecursiveIteratorIteratorponieważ jest to wspólne z innymi typami, ale podałem kilka informacji technicznych, jak to faktycznie działa. Myślę, że przykłady dobrze pokazują różnice: rodzaj iteracji jest główną różnicą między nimi. Nie mam pojęcia, jeśli kupisz typ iteracji, który ubijesz trochę inaczej, ale IMHO nie jest łatwe z semantyką typów iteracji.
hakre

1
Pierwsza część została nieco ulepszona, ale gdy zaczniesz przechodzić do przykładów, nadal skręca ona w faktyczne nieścisłości. Gdyby przyciąć odpowiedź na zasadzie poziomej, byłoby to znacznie lepsze.
salathe

32

Jaka jest różnica między IteratorIteratori RecursiveIteratorIterator?

Aby zrozumieć różnicę między tymi dwoma iteratorami, należy najpierw trochę zrozumieć stosowane konwencje nazewnictwa i co rozumiemy przez iteratory „rekurencyjne”.

Iteratory rekurencyjne i nierekurencyjne

PHP ma nie „rekurencyjne” iteratory, takie jak ArrayIteratori FilesystemIterator. Istnieją również „rekurencyjne” iteratory, takie jak RecursiveArrayIteratoriRecursiveDirectoryIterator . Ci drudzy mają metody umożliwiające ich wnikanie, ci pierwsi nie.

Gdy instancje tych iteratorów są zapętlone samodzielnie, nawet te rekurencyjne, wartości pochodzą tylko z „najwyższego” poziomu, nawet jeśli są zapętlone po zagnieżdżonej tablicy lub katalogu z podkatalogami.

Iteratory rekurencyjne implementują zachowanie rekurencyjne (via hasChildren(), getChildren()), ale tego nie robią wykorzystują .

Lepiej byłoby myśleć o iteratorach rekurencyjnych jako o iteratorach „rekurencyjnych”, mają one rozszerzenie zdolność do iteracji rekurencyjnej, ale zwykłe iterowanie po instancji jednej z tych klas tego nie zrobi. Aby wykorzystać zachowanie rekurencyjne, czytaj dalej.

RecursiveIteratorIterator

Tutaj właśnie RecursiveIteratorIteratorpojawia się gra. Posiada wiedzę na temat wywoływania „rekurencyjnych” iteratorów w taki sposób, aby drążyć strukturę w normalnej, płaskiej pętli. Wprowadza rekurencyjne zachowanie do działania. Zasadniczo wykonuje pracę polegającą na przechodzeniu przez każdą z wartości w iteratorze, sprawdzaniu, czy są „dzieci”, do których można się powrócić, czy nie, oraz wchodzeniu i wychodzeniu z tych zbiorów dzieci. Wbijasz wystąpienie RecursiveIteratorIteratorw foreach, a on nurkuje w strukturze, więc nie musisz tego robić.

Gdyby RecursiveIteratorIteratornie zostało użyte, musiałbyś napisać własne pętle rekurencyjne, aby wykorzystać zachowanie rekurencyjne, sprawdzając iterator „rekurencyjny” hasChildren()i używając getChildren().

Więc to jest krótkie omówienie RecursiveIteratorIterator, czym się różni od IteratorIterator? Cóż, w zasadzie zadajesz to samo pytanie, co Jaka jest różnica między kotkiem a drzewem? To, że oba pojawiają się w tej samej encyklopedii (lub podręczniku dla iteratorów), nie oznacza, że ​​powinieneś się pomylić między nimi.

IteratorIterator

Zadaniem tego IteratorIteratorjest pobranie dowolnego Traversableobiektu i owinięcie go tak, aby spełniał wymagania Iteratorinterfejsu. Służy do tego możliwość zastosowania zachowania specyficznego dla iteratora na obiekcie niebędącym iteratorem.

Aby dać praktyczny przykład, DatePeriodklasa jest, Traversableale nie jest Iterator. W związku z tym możemy zapętlić jego wartości za pomocą, foreach()ale nie możemy zrobić innych rzeczy, które normalnie wykonalibyśmy za pomocą iteratora, takich jak filtrowanie.

ZADANIE : Powtarzaj w poniedziałki, środy i piątki przez następne cztery tygodnie.

Tak, jest to trywialne foreachomijanie DatePeriodi używanie if()w pętli; ale nie o to chodzi w tym przykładzie!

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

Powyższy fragment kodu nie zadziała, ponieważ CallbackFilterIteratoroczekuje wystąpienia klasy implementującej Iteratorinterfejs, a DatePeriodnie. Ponieważ jednak tak jest Traversable, możemy łatwo spełnić to wymaganie, używając IteratorIterator.

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

Jak widać, nie ma to nic wspólnego z iteracją po klasach iteratorów ani rekursją, i na tym polega różnica między IteratorIteratori RecursiveIteratorIterator.

Podsumowanie

RecursiveIteraratorIteratorsłuży do iteracji po RecursiveIterator(„rekurencyjnym” iteratorze), wykorzystując dostępne zachowanie rekurencyjne.

IteratorIteratorsłuży do stosowania Iteratorzachowania do Traversableobiektów , które nie są iteratorami .


Czy nie jest to IteratorIteratortylko standardowy typ przechodzenia w kolejności liniowej dla Traversableobiektów? Te, które mogłyby być użyte bez tego, tak foreachjak jest? A nawet dalej, czy nie jest RecursiveIterator zawsze a, Traversablea zatem nie tylko, IteratorIteratorale także RecursiveIteratorIteratorzawsze „do stosowania Iteratorzachowania do obiektów nie-iteracyjnych, przemieszczalnych” ? (Powiedziałbym teraz, że foreachstosuje typ iteracji za pośrednictwem obiektu iteratora na obiektach kontenera, które implementują interfejs typu iteratora, więc są to obiekty-kontenera-iteratora, zawsze Traversable)
hakre

Jak stwierdza moja odpowiedź, IteratorIteratorto klasa, która polega na zawijaniu Traversableobiektów w plik Iterator. Nic więcej . Wydaje się, że używasz tego terminu bardziej ogólnie.
salathe

Pozornie pouczająca odpowiedź. Jedno pytanie, czy RecursiveIteratorIterator również nie zawijałby obiektów, aby miały również dostęp do zachowania Iteratora? Jedyna różnica między nimi polegałaby na tym, że RecursiveIteratorIterator może drążyć w dół, podczas gdy IteratorIterator nie może?
Mike Purcell,

@ salathe, czy wiesz, dlaczego iterator rekurencyjny (RecursiveDirectoryIterator) nie implementuje zachowania hasChildren (), getChildren ()?
anru

8
+1 za powiedzenie „rekurencyjny”. Nazwa przez długi czas zwodziła mnie, ponieważ Recursivein RecursiveIteratorsugeruje zachowanie, podczas gdy bardziej odpowiednia byłaby nazwa opisująca zdolności, np RecursibleIterator.
koza

0

W przypadku użycia z iterator_to_array(), RecursiveIteratorIteratorbędzie rekurencyjnie przeszukiwać tablicę, aby znaleźć wszystkie wartości. Oznacza to, że spłaszczy oryginalną tablicę.

IteratorIterator zachowa pierwotną strukturę hierarchiczną.

Ten przykład jasno pokaże różnicę:

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

To jest całkowicie mylące. new IteratorIterator(new ArrayIterator($array))to znaczy new ArrayIterator($array), że strona zewnętrzna IteratorIteratornic nie robi. Co więcej, spłaszczanie wyniku nie ma z tym nic wspólnego iterator_to_array- po prostu przekształca iterator w tablicę. Spłaszczenie jest właściwością sposobu, w jaki RecursiveArrayIteratorprzechodzi jego wewnętrzny iterator.
Pytania dotyczące Quolonel

0

RecursiveDirectoryIterator wyświetla całą nazwę ścieżki, a nie tylko nazwę pliku. Reszta wygląda dobrze. Dzieje się tak, ponieważ nazwy plików są generowane przez SplFileInfo. Zamiast tego powinny być wyświetlane jako nazwa basenowa. Żądane wyjście jest następujące:

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

wynik:

tree
 ├ dirA
 │ ├ dirB
 │ │ └ fileD
 │ ├ fileB
 │ └ fileC
 └ fileA
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.