Bagaimana cara kerja RecursiveIteratorIterator di PHP?


88

Bagaimana cara RecursiveIteratorIteratorkerjanya?

Manual PHP tidak banyak didokumentasikan atau dijelaskan. Apa perbedaan antara IteratorIteratordan RecursiveIteratorIterator?


2
ada contoh di php.net/manual/en/recursiveiteratoriterator.construct.php dan ada juga Pengenalan di php.net/manual/en/class.iteratoriterator.php - dapatkah Anda menunjukkan apa yang sebenarnya sulit Anda pahami . Apa yang harus dimuat dalam Manual agar lebih mudah dipahami?
Gordon

1
Jika Anda bertanya bagaimana cara RecursiveIteratorIteratorkerjanya, apakah Anda sudah mengerti cara IteratorIteratorkerjanya? Maksud saya pada dasarnya sama, hanya antarmuka yang dikonsumsi oleh keduanya yang berbeda. Dan apakah Anda lebih tertarik pada beberapa contoh atau Anda ingin melihat perbedaan dari implementasi kode C yang mendasarinya?
hakre

@Gordon Saya tidak yakin bagaimana loop foreach tunggal dapat melintasi semua elemen dalam struktur pohon
varuog

@hakra Saya sekarang mencoba untuk mempelajari semua built-in antarmuka serta antarmuka spl dan implementasi iterator. Saya tertarik untuk mengetahui bagaimana cara kerjanya di latar belakang dengan forach loop dengan beberapa contoh.
varuog

@hakre Mereka berdua sebenarnya sangat berbeda. IteratorIteratorpeta Iteratordan IteratorAggregatemenjadi Iterator, di mana REcusiveIteratorIteratordigunakan untuk melintasi recusivly aRecursiveIterator
Adam

Jawaban:


251

RecursiveIteratorIteratoradalah traversal pohonIterator pelaksana beton . Ini memungkinkan programmer untuk melintasi objek kontainer yang mengimplementasikan antarmuka, lihat Iterator di Wikipedia untuk prinsip umum, jenis, semantik, dan pola iterator.RecursiveIterator

Dalam perbedaan IteratorIteratoryang merupakan Iteratortraversal objek implementasi konkret dalam urutan linier (dan secara default menerima semua jenis Traversabledalam konstruktornya), RecursiveIteratorIteratorperulangan memungkinkan atas semua node dalam pohon objek yang diurutkan dan konstruktornya mengambil a RecursiveIterator.

Singkatnya: RecursiveIteratorIteratormemungkinkan Anda untuk melakukan loop di atas pohon, IteratorIteratormemungkinkan Anda untuk melakukan loop di atas daftar. Saya tunjukkan dengan beberapa contoh kode di bawah ini segera.

Secara teknis, ini bekerja dengan mendobrak linieritas dengan melintasi semua turunan node (jika ada). Hal ini dimungkinkan karena menurut definisi semua anak dari sebuah node kembali a RecursiveIterator. Tingkat atas Iteratorkemudian secara internal menumpuk RecursiveIterators yang berbeda berdasarkan kedalamannya dan menyimpan penunjuk ke sub aktif saat ini Iteratoruntuk traversal.

Ini memungkinkan untuk mengunjungi semua simpul pohon.

Prinsip dasarnya sama dengan IteratorIterator: Antarmuka menentukan jenis iterasi dan kelas iterator dasar adalah implementasi dari semantik ini. Bandingkan dengan contoh di bawah ini, untuk perulangan linier dengan foreachAnda biasanya tidak terlalu memikirkan detail implementasi kecuali Anda perlu mendefinisikan yang baru Iterator(misalnya ketika beberapa jenis konkret itu sendiri tidak diimplementasikan Traversable).

Untuk traversal rekursif - kecuali jika Anda tidak menggunakan Traversaliterasi traversal yang ditentukan sebelumnya yang sudah memiliki iterasi traversal rekursif - Anda biasanya perlu membuat instance RecursiveIteratorIteratoriterasi yang ada atau bahkan menulis iterasi traversal rekursif yang TraversableAnda miliki untuk memiliki jenis iterasi traversal ini foreach.

Tip: Anda mungkin tidak menerapkan yang satu atau yang lain milik Anda, jadi ini mungkin sesuatu yang layak dilakukan untuk pengalaman praktis Anda tentang perbedaan yang mereka miliki. Anda menemukan saran DIY di akhir jawaban.

Perbedaan teknis singkatnya:

  • Sementara IteratorIteratormengambil apapun Traversableuntuk linier traversal, RecursiveIteratorIteratormembutuhkan lebih spesifik RecursiveIteratoruntuk melakukan loop di atas pohon.
  • Dimana IteratorIteratormengekspos utamanya Iteratormelalui getInnerIerator(), RecursiveIteratorIteratormenyediakan sub- Iteratorhanya aktif saat ini melalui metode itu.
  • Sementara IteratorIteratorsama sekali tidak menyadari apa pun seperti orang tua atau anak, RecursiveIteratorIteratortahu bagaimana cara mendapatkan dan melintasi anak juga.
  • IteratorIteratortidak membutuhkan tumpukan iterator, RecursiveIteratorIteratormemiliki tumpukan seperti itu dan mengetahui sub-iterator yang aktif.
  • Dimana IteratorIteratormemiliki urutannya karena linieritas dan tidak ada pilihan, RecursiveIteratorIteratormemiliki pilihan untuk traversal lebih lanjut dan perlu memutuskan per setiap node (diputuskan melalui mode perRecursiveIteratorIterator ).
  • RecursiveIteratorIteratormemiliki lebih banyak metode daripada IteratorIterator.

Untuk meringkas: RecursiveIteratoradalah jenis konkret dari iterasi (perulangan di atas pohon) yang bekerja pada iteratornya sendiri, yaitu RecursiveIterator. Itu adalah prinsip dasar yang sama seperti dengan IteratorIerator, tetapi jenis iterasinya berbeda (urutan linier).

Idealnya, Anda juga dapat membuat set sendiri. Satu-satunya hal yang perlu adalah bahwa iterator Anda mengimplementasikan Traversableyang mungkin dilakukan melalui Iteratoratau IteratorAggregate. Kemudian Anda bisa menggunakannya dengan foreach. Misalnya beberapa jenis objek iterasi rekursif traversal terner pohon bersama dengan antarmuka iterasi yang sesuai untuk objek kontainer.


Mari kita ulas dengan beberapa contoh kehidupan nyata yang tidak terlalu abstrak. Antara antarmuka, iterator beton, objek kontainer, dan semantik iterasi, ini mungkin bukan ide yang buruk.

Ambil daftar direktori sebagai contoh. Pertimbangkan Anda telah mendapatkan file dan pohon direktori berikut pada disk:

Pohon Direktori

Sementara iterator dengan urutan linier hanya melintasi folder dan file tingkat atas (daftar direktori tunggal), iterator rekursif juga melintasi subfolder dan mencantumkan semua folder dan file (daftar direktori dengan daftar subdirektorinya):

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

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

Anda dapat dengan mudah membandingkan ini dengan IteratorIteratoryang tidak melakukan rekursi untuk melintasi pohon direktori. Dan RecursiveIteratorIteratoryang dapat melintasi pohon seperti yang ditunjukkan daftar Rekursif.

Pada awalnya contoh yang sangat mendasar dengan DirectoryIteratoryang mengimplementasikan Traversableyang memungkinkan foreachuntuk mengulanginya :

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

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

Keluaran contoh untuk struktur direktori di atas adalah:

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

Seperti yang Anda lihat, ini belum menggunakan IteratorIteratoratau RecursiveIteratorIterator. Sebaliknya itu hanya menggunakan foreachyang beroperasi pada Traversableantarmuka.

Karena foreachsecara default hanya mengetahui jenis iterasi bernama urutan linier, kita mungkin ingin menentukan jenis iterasi secara eksplisit. Sekilas mungkin tampak terlalu bertele-tele, tetapi untuk tujuan demonstrasi (dan untuk membuat perbedaan dengan RecursiveIteratorIteratorlebih terlihat nanti), mari kita tentukan tipe linier dari iterasi secara eksplisit menentukan IteratorIteratorjenis iterasi untuk daftar direktori:

$files = new IteratorIterator($dir);

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

Contoh ini hampir identik dengan yang pertama, perbedaannya $filesadalah sekarang menjadi IteratorIteratorjenis iterasi untuk Traversable $dir:

$files = new IteratorIterator($dir);

Seperti biasa tindakan iterasi dilakukan oleh foreach:

foreach ($files as $file) {

Outputnya persis sama. Jadi apa bedanya? Yang berbeda adalah objek yang digunakan di dalam foreach. Dalam contoh pertama ini adalah a, DirectoryIteratordalam contoh kedua itu adalah IteratorIterator. Ini menunjukkan fleksibilitas yang dimiliki iterator: Anda dapat menggantinya satu sama lain, kode di dalamnya foreachterus berfungsi seperti yang diharapkan.

Mari mulai mendapatkan seluruh daftar, termasuk subdirektori.

Karena sekarang kita telah menentukan jenis iterasi, mari pertimbangkan untuk mengubahnya ke jenis iterasi lain.

Kami tahu kami perlu melintasi seluruh pohon sekarang, tidak hanya tingkat pertama. Untuk memiliki pekerjaan dengan sederhana foreachkita membutuhkan berbagai jenis iterator: RecursiveIteratorIterator. Dan yang satu itu hanya dapat melakukan iterasi pada objek kontainer yang memiliki RecursiveIteratorantarmuka .

Antarmuka adalah kontrak. Setiap kelas yang mengimplementasikannya dapat digunakan bersama dengan RecursiveIteratorIterator. Contoh dari kelas tersebut adalah the RecursiveDirectoryIterator, yang merupakan varian rekursif dari DirectoryIterator.

Mari kita lihat contoh kode pertama sebelum menulis kalimat lain dengan kata-I:

$dir  = new RecursiveDirectoryIterator($path);

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

Contoh ketiga ini hampir identik dengan yang pertama, namun menghasilkan beberapa keluaran yang berbeda:

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

Oke, tidak jauh berbeda, nama file sekarang berisi nama jalur di depan, tetapi sisanya terlihat serupa juga.

Seperti yang ditunjukkan contoh, bahkan objek direktori sudah mengimplementasikan RecursiveIteratorantarmuka, ini belum cukup untuk foreachmelintasi seluruh pohon direktori. Di sinilah RecursiveIteratorIteratorberaksi. Contoh 4 menunjukkan bagaimana:

$files = new RecursiveIteratorIterator($dir);

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

Menggunakan RecursiveIteratorIteratoralih - alih hanya $dirobjek sebelumnya akan membuat foreachmelintasi semua file dan direktori secara rekursif. Ini kemudian mencantumkan semua file, karena jenis iterasi objek telah ditentukan sekarang:

[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

Ini seharusnya sudah menunjukkan perbedaan antara traversal datar dan pohon. The RecursiveIteratorIteratormampu melintasi setiap struktur seperti pohon sebagai daftar elemen. Karena terdapat lebih banyak informasi (seperti level yang dilakukan iterasi saat ini), dimungkinkan untuk mengakses objek iterator sambil mengulanginya dan misalnya mengindentasi output:

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

Dan keluaran dari Contoh 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

Tentu ini tidak memenangkan kontes kecantikan, tetapi ini menunjukkan bahwa dengan iterator rekursif ada lebih banyak informasi yang tersedia daripada hanya urutan linier kunci dan nilai . Bahkan foreachhanya dapat mengekspresikan linieritas semacam ini, mengakses iterator itu sendiri memungkinkan untuk memperoleh lebih banyak informasi.

Mirip dengan meta-informasi, ada juga cara berbeda yang memungkinkan bagaimana melintasi pohon dan karenanya mengurutkan keluaran. Ini adalah mode dariRecursiveIteratorIterator dan dapat diatur dengan konstruktor.

Contoh selanjutnya akan memberitahu RecursiveDirectoryIteratoruntuk menghapus entri titik ( .dan ..) karena kita tidak membutuhkannya. Tetapi juga mode rekursi akan diubah untuk mengambil elemen induk (subdirektori) terlebih dahulu ( SELF_FIRST) sebelum anak-anak (file dan sub-subdirektori di subdirektori):

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

Output sekarang menunjukkan entri subdirektori terdaftar dengan benar, jika Anda membandingkan dengan output sebelumnya yang tidak ada:

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

Karena itu, mode rekursi mengontrol apa dan kapan brach atau leaf di pohon dikembalikan, untuk contoh direktori:

  • LEAVES_ONLY (default): Hanya daftar file, tidak ada direktori.
  • SELF_FIRST (atas): Daftar direktori dan kemudian file-file di sana.
  • CHILD_FIRST (tanpa contoh): Buat daftar file di subdirektori terlebih dahulu, lalu direktori.

Output dari Contoh 5 dengan dua mode lainnya:

  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

Jika Anda membandingkannya dengan traversal standar, semua hal ini tidak tersedia. Oleh karena itu, iterasi rekursif sedikit lebih kompleks ketika Anda perlu membungkusnya, namun mudah digunakan karena berperilaku seperti iterator, Anda memasukkannya ke dalam foreachdan selesai.

Saya pikir ini adalah contoh yang cukup untuk satu jawaban. Anda dapat menemukan kode sumber lengkap serta contoh untuk menampilkan ascii-tree yang bagus di intinya: https://gist.github.com/3599532

Lakukan Sendiri: Buat RecursiveTreeIteratorPekerjaan Baris demi Baris.

Contoh 5 menunjukkan bahwa ada meta-informasi tentang status iterator yang tersedia. Namun, ini sengaja ditunjukkan dalam yang foreachiterasi. Dalam kehidupan nyata, ini secara alami termasuk di dalam RecursiveIterator.

Contoh yang lebih baik adalah RecursiveTreeIterator, ini menangani indentasi, awalan, dan sebagainya. Lihat fragmen kode berikut:

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

Ini RecursiveTreeIteratordimaksudkan untuk bekerja baris demi baris, hasilnya cukup lurus ke depan dengan satu masalah kecil:

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

Saat digunakan dalam kombinasi dengan a, RecursiveDirectoryIteratorini akan menampilkan seluruh nama jalur dan bukan hanya nama file. Sisanya terlihat bagus. Ini karena nama file dibuat oleh SplFileInfo. Itu harus ditampilkan sebagai nama dasar sebagai gantinya. Output yang diinginkan adalah sebagai berikut:

/// Solved ///

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

Buat kelas dekorator yang bisa digunakan dengan RecursiveTreeIteratorsebagai pengganti RecursiveDirectoryIterator. Ini harus memberikan nama dasar saat ini, SplFileInfobukan nama jalur. Fragmen kode terakhir akan terlihat seperti ini:

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

Fragmen-fragmen ini termasuk $unicodeTreePrefixmerupakan bagian dari inti dalam Lampiran: Lakukan Sendiri: Buat RecursiveTreeIteratorPekerjaan Baris demi Baris. .


Itu tidak menjawab pertanyaan yang diajukan, mengandung kesalahan faktual dan kehilangan poin inti saat Anda melanjutkan untuk menyesuaikan iterasi. Secara keseluruhan, ini tampak seperti upaya yang buruk untuk mendapatkan hadiah pada topik yang tidak Anda ketahui banyak atau, jika Anda tahu, tidak dapat menyaring menjadi jawaban atas pertanyaan yang diajukan.
salathe

2
Yang tidak menjawab pertanyaan "mengapa" saya, Anda hanya membuat lebih banyak kata tanpa banyak bicara. Mungkin Anda mulai dengan Kesalahan yang diperhitungkan? Tunjuklah, jangan merahasiakannya.
hakre

Kalimat pertama salah: "RecursiveIteratorIterator adalah IteratorIterator yang mendukung…", ini tidak benar.
salathe

1
@ salathe: Terima kasih atas tanggapan Anda. Saya mengedit jawaban untuk alamat itu. Kalimat pertama memang salah dan tidak lengkap. Saya masih meninggalkan detail implementasi konkret RecursiveIteratorIteratorkarena ini sama dengan jenis lain tetapi saya memberikan beberapa info teknis tentang cara kerjanya sebenarnya. Contoh yang menurut saya menunjukkan perbedaannya dengan baik: jenis iterasi adalah perbedaan utama di antara keduanya. Tidak tahu jika Anda membeli jenis iterasi yang Anda koin itu sedikit berbeda tetapi IMHO tidak mudah dengan jenis iterasi semantik yang dimiliki.
hakre

1
Bagian pertama agak diperbaiki, tetapi begitu Anda mulai beralih ke contoh, itu masih berubah menjadi ketidakakuratan faktual. Jika Anda memotong jawaban pada aturan horizontal, itu akan jauh lebih baik.
salathe

32

Apa perbedaan dari IteratorIteratordan RecursiveIteratorIterator?

Untuk memahami perbedaan antara kedua iterator ini, pertama-tama kita harus memahami sedikit tentang konvensi penamaan yang digunakan dan apa yang kami maksud dengan iterator "rekursif".

Iterator rekursif dan non-rekursif

PHP memiliki iterator non- "rekursif", seperti ArrayIteratordan FilesystemIterator. Ada juga iterator "rekursif" seperti RecursiveArrayIteratordan RecursiveDirectoryIterator. Yang terakhir memiliki metode yang memungkinkan mereka untuk dibor, yang pertama tidak.

Ketika instance dari iterator ini di-loop sendiri, bahkan yang rekursif, nilainya hanya datang dari level "top" meskipun melakukan looping pada array atau direktori bersarang dengan sub-direktori.

Iterator rekursif mengimplementasikan perilaku rekursif (via hasChildren(), getChildren()) tetapi tidak mengeksploitasinya .

Mungkin lebih baik untuk menganggap iterator rekursif sebagai iterator "rekursif", mereka memiliki kemampuan untuk diiterasi secara rekursif tetapi hanya melakukan iterasi pada sebuah instance dari salah satu kelas ini tidak akan melakukannya. Untuk memanfaatkan perilaku rekursif, teruslah membaca.

RecursiveIteratorIterator

Di sinilah RecursiveIteratorIteratormasuk untuk bermain. Ia memiliki pengetahuan tentang bagaimana memanggil iterator yang "berulang" sedemikian rupa untuk menelusuri ke dalam struktur dalam loop normal, datar. Ini menempatkan perilaku rekursif ke dalam tindakan. Ini pada dasarnya melakukan pekerjaan untuk melangkahi setiap nilai dalam iterator, mencari untuk melihat apakah ada "anak-anak" untuk muncul kembali atau tidak, dan masuk dan keluar dari koleksi anak-anak itu. Anda memasukkan contoh RecursiveIteratorIteratorke depan, dan itu menyelam ke dalam struktur sehingga Anda tidak perlu melakukannya.

Jika RecursiveIteratorIteratortidak digunakan, Anda harus menulis loop rekursif Anda sendiri untuk mengeksploitasi perilaku rekursif, memeriksa iterator "rekursif" hasChildren()dan menggunakan getChildren().

Nah itulah gambaran singkat tentang RecursiveIteratorIterator, apa bedanya dengan IteratorIterator? Nah, pada dasarnya Anda menanyakan pertanyaan yang sama seperti Apa perbedaan antara anak kucing dan pohon? Hanya karena keduanya muncul dalam ensiklopedia yang sama (atau manual, untuk iterator) tidak berarti Anda harus bingung di antara keduanya.

IteratorIterator

Tugasnya IteratorIteratoradalah mengambil Traversableobjek apa pun , dan membungkusnya sedemikian rupa sehingga memenuhi Iteratorantarmuka. Kegunaannya adalah untuk kemudian dapat menerapkan perilaku khusus iterator pada objek non-iterator.

Untuk memberikan contoh praktis, DatePeriodkelas tersebut Traversablebukan Iterator. Dengan demikian, kita dapat mengulang nilainya dengan foreach()tetapi tidak dapat melakukan hal-hal lain yang biasanya kita lakukan dengan iterator, seperti pemfilteran.

TUGAS : Ulangi hari Senin, Rabu, dan Jumat dalam empat minggu berikutnya.

Ya, ini sepele dengan- foreachmelewati DatePerioddan menggunakan if()dalam loop; tapi bukan itu inti dari contoh ini!

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

Cuplikan di atas tidak akan berfungsi karena CallbackFilterIteratormengharapkan instance dari kelas yang mengimplementasikan Iteratorantarmuka, DatePeriodpadahal tidak. Namun, karena itu Traversablekami dapat dengan mudah memenuhi kebutuhan itu dengan menggunakan IteratorIterator.

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

Seperti yang Anda lihat, ini tidak ada hubungannya sama sekali dengan iterasi kelas iterator atau rekursi, dan di situlah letak perbedaan antara IteratorIteratordan RecursiveIteratorIterator.

Ringkasan

RecursiveIteraratorIteratoradalah untuk melakukan iterasi RecursiveIterator(iterator "berulang"), mengeksploitasi perilaku rekursif yang tersedia.

IteratorIteratoradalah untuk menerapkan Iteratorperilaku ke objek non-iterator Traversable.


Bukankah IteratorIteratorhanya tipe standar dari traversal tatanan linier untuk Traversableobjek? Apa yang bisa digunakan tanpa itu foreachapa adanya? Dan lebih jauh lagi, bukankah RecursiveIterator selalu a Traversabledan karena itu tidak hanya IteratorIteratortetapi juga RecursiveIteratorIteratorselalu "untuk menerapkan Iteratorperilaku ke non-iterator, objek Traversable" ? (Sekarang saya akan mengatakan foreachmenerapkan tipe iterasi melalui objek iterator pada objek kontainer yang mengimplementasikan antarmuka tipe iterator jadi ini adalah objek-kontainer-iterator, selalu Traversable)
hakre

Seperti jawaban saya menyatakan, IteratorIteratoradalah kelas yang semuanya tentang membungkus Traversableobjek dalam file Iterator. Tidak lebih . Anda tampaknya menerapkan istilah tersebut secara lebih umum.
salathe

Jawaban yang tampak informatif. Satu pertanyaan, bukankah RecursiveIteratorIterator juga membungkus objek sehingga mereka juga memiliki akses ke perilaku Iterator? Satu-satunya perbedaan antara keduanya adalah bahwa RecursiveIteratorIterator dapat menelusuri, sedangkan IteratorIterator tidak bisa?
Mike Purcell

@salathe, tahukah Anda mengapa rekursif iterator (RecursiveDirectoryIterator) tidak mengimplementasikan perilaku hasChildren (), getChildren ()?
Anru

8
1 untuk mengatakan "recursible". Nama itu menyesatkan saya untuk waktu yang lama karena Recursivein RecursiveIteratormenyiratkan perilaku, sedangkan nama yang lebih cocok adalah yang menggambarkan kemampuan, seperti RecursibleIterator.
kambing

0

Saat digunakan dengan iterator_to_array(), RecursiveIteratorIteratorakan secara rekursif menjalankan array untuk menemukan semua nilai. Artinya itu akan meratakan array aslinya.

IteratorIterator akan mempertahankan struktur hierarki asli.

Contoh ini akan menunjukkan dengan jelas perbedaannya:

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

Ini sangat menyesatkan. new IteratorIterator(new ArrayIterator($array))setara dengan new ArrayIterator($array), yaitu, bagian luar IteratorIteratortidak melakukan apa pun. Selain itu, perataan output tidak ada hubungannya dengan iterator_to_array- itu hanya mengubah iterator menjadi array. Perataan adalah sifat dari cara RecursiveArrayIteratorberjalan iterator bagian dalamnya.
Pertanyaan Quolonel

0

RecursiveDirectoryIterator menampilkan seluruh nama jalur dan bukan hanya nama file. Sisanya terlihat bagus. Ini karena nama file dibuat oleh SplFileInfo. Itu harus ditampilkan sebagai nama dasar sebagai gantinya. Output yang diinginkan adalah sebagai berikut:

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

keluaran:

tree
 ├ dirA
 │ ├ dirB
 │ │ └ fileD
 │ ├ fileB
 │ └ fileC
 └ fileA
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.