วิธีการRecursiveIteratorIterator
ทำงานหรือไม่
คู่มือ PHP ไม่มีเอกสารหรือคำอธิบายมากนัก อะไรคือความแตกต่างระหว่างIteratorIterator
และRecursiveIteratorIterator
?
วิธีการRecursiveIteratorIterator
ทำงานหรือไม่
คู่มือ PHP ไม่มีเอกสารหรือคำอธิบายมากนัก อะไรคือความแตกต่างระหว่างIteratorIterator
และRecursiveIteratorIterator
?
RecursiveIteratorIterator
ทำงานอย่างไรคุณเข้าใจวิธีการIteratorIterator
ทำงานแล้วหรือยัง? ฉันหมายความว่ามันเหมือนกันโดยทั่วไปมีเพียงอินเทอร์เฟซที่ใช้โดยทั้งสองเท่านั้นที่แตกต่างกัน และคุณสนใจตัวอย่างบางส่วนมากขึ้นหรือคุณต้องการเห็นความแตกต่างของการใช้งานโค้ด C หรือไม่?
IteratorIterator
แผนที่Iterator
และIteratorAggregate
เข้าไปในIterator
ตำแหน่งที่REcusiveIteratorIterator
ใช้ในการสำรวจซ้ำRecursiveIterator
คำตอบ:
RecursiveIteratorIterator
เป็นรูปธรรมIterator
การดำเนินการสำรวจเส้นทางต้นไม้ ช่วยให้โปรแกรมเมอร์สามารถสำรวจวัตถุคอนเทนเนอร์ที่ใช้RecursiveIterator
อินเทอร์เฟซได้โปรดดูIterator ใน Wikipediaสำหรับหลักการทั่วไปประเภทความหมายและรูปแบบของตัววนซ้ำ
ในความแตกต่างIteratorIterator
ที่เป็นคอนกรีตที่Iterator
ใช้การข้ามผ่านวัตถุตามลำดับเชิงเส้น (และโดยค่าเริ่มต้นจะยอมรับชนิดใด ๆTraversable
ในตัวสร้าง) การRecursiveIteratorIterator
อนุญาตให้วนซ้ำบนโหนดทั้งหมดในโครงสร้างของวัตถุที่เรียงลำดับและตัวสร้างจะใช้เวลา a RecursiveIterator
.
ในระยะสั้น: RecursiveIteratorIterator
ช่วยให้คุณวนรอบต้นไม้IteratorIterator
ช่วยให้คุณวนซ้ำรายการได้ ฉันแสดงให้เห็นพร้อมตัวอย่างโค้ดด้านล่างเร็ว ๆ นี้
ในทางเทคนิคจะได้ผลโดยการแยกออกจากความเป็นเชิงเส้นโดยการข้ามลูกของโหนดทั้งหมด (ถ้ามี) สิ่งนี้เป็นไปได้เพราะตามนิยามชายด์ทั้งหมดของโหนดเป็น a RecursiveIterator
. Iterator
จากนั้นระดับบนสุดจะเรียงซ้อนRecursiveIterator
s ที่แตกต่างกันภายในโดยความลึกและเก็บตัวชี้ไปยังส่วนย่อยที่ใช้งานอยู่ในปัจจุบันIterator
สำหรับการข้ามผ่าน
สิ่งนี้ช่วยให้สามารถเยี่ยมชมโหนดทั้งหมดของต้นไม้ได้
หลักการพื้นฐานจะเหมือนกับIteratorIterator
: อินเทอร์เฟซระบุประเภทของการวนซ้ำและคลาสตัววนซ้ำพื้นฐานคือการนำความหมายเหล่านี้ไปใช้ เปรียบเทียบกับตัวอย่างด้านล่างสำหรับการวนซ้ำเชิงเส้นโดยforeach
ปกติคุณไม่ได้คิดถึงรายละเอียดการใช้งานมากนักเว้นแต่คุณจะต้องกำหนดใหม่Iterator
(เช่นเมื่อคอนกรีตบางประเภทไม่ได้ใช้งานTraversable
)
สำหรับการสำรวจเส้นทาง recursive - ถ้าคุณไม่ได้ใช้ที่กำหนดไว้ล่วงหน้าTraversal
ที่มีอยู่แล้ว recursive สำรวจเส้นทางซ้ำ - โดยปกติคุณจะต้องอินสแตนซ์ที่มีอยู่RecursiveIteratorIterator
ซ้ำหรือแม้กระทั่งเขียน recursive สำรวจเส้นทางย้ำว่าเป็นของคุณเองที่จะมีประเภทของการสำรวจเส้นทางนี้ซ้ำกับTraversable
foreach
เคล็ดลับ:คุณอาจไม่ได้ใช้อย่างใดอย่างหนึ่งหรือใช้ของคุณเองดังนั้นนี่อาจเป็นสิ่งที่คุ้มค่าที่จะทำเพื่อประสบการณ์จริงของความแตกต่างที่พวกเขามี คุณจะพบคำแนะนำ DIY ในตอนท้ายของคำตอบ
ความแตกต่างทางเทคนิคโดยย่อ:
IteratorIterator
จะใช้เวลาใด ๆTraversable
สำหรับการส่งผ่านเชิงเส้น แต่RecursiveIteratorIterator
ต้องการความเฉพาะเจาะจงมากขึ้นRecursiveIterator
ในการวนซ้ำต้นไม้IteratorIterator
exposes หลักIterator
ผ่านgetInnerIerator()
, RecursiveIteratorIterator
ให้ย่อยที่ใช้งานอยู่ในปัจจุบันIterator
เพียงผ่านวิธีการที่IteratorIterator
ไม่รู้อะไรเลยเหมือนพ่อแม่หรือลูก แต่RecursiveIteratorIterator
ก็รู้วิธีรับและสำรวจเด็กด้วยเช่นกันIteratorIterator
ไม่จำเป็นต้องมีสแต็กของตัวทำซ้ำRecursiveIteratorIterator
มีสแต็กดังกล่าวและรู้จักตัวทำซ้ำย่อยที่ใช้งานอยู่IteratorIterator
มีลำดับเนื่องจากความเป็นเชิงเส้นและไม่มีทางเลือกRecursiveIteratorIterator
มีทางเลือกสำหรับการข้ามผ่านเพิ่มเติมและจำเป็นต้องตัดสินใจต่อแต่ละโหนด (ตัดสินใจผ่านโหมดต่อRecursiveIteratorIterator
)RecursiveIteratorIterator
IteratorIterator
มีวิธีการมากกว่าเพื่อสรุป: RecursiveIterator
เป็นชนิดที่เป็นรูปธรรมของการทำซ้ำ (วนลูปกับต้นไม้) ที่ทำงานบน iterators RecursiveIterator
ของตัวเองคือ นั่นเป็นหลักการพื้นฐานเช่นเดียวกับIteratorIerator
แต่ประเภทของการวนซ้ำแตกต่างกัน (ลำดับเชิงเส้น)
คุณสามารถสร้างชุดของคุณเองได้เช่นกัน สิ่งเดียวที่จำเป็นก็คือว่าการดำเนินการของคุณ iterator Traversable
ซึ่งเป็นไปได้ทางหรือIterator
IteratorAggregate
จากนั้นคุณสามารถใช้กับforeach
. ตัวอย่างเช่นออบเจ็กต์การวนซ้ำแบบวนซ้ำแบบวนซ้ำแบบ ternary tree ร่วมกับอินเทอร์เฟซการวนซ้ำตามสำหรับอ็อบเจ็กต์คอนเทนเนอร์
ลองทบทวนกับตัวอย่างชีวิตจริงที่ไม่ได้เป็นนามธรรม ระหว่างอินเทอร์เฟซตัวทำซ้ำคอนกรีตวัตถุคอนเทนเนอร์และความหมายการวนซ้ำนี่อาจไม่ใช่ความคิดที่ไม่ดี
ยกตัวอย่างรายการไดเร็กทอรี พิจารณาว่าคุณมีไฟล์และแผนผังไดเร็กทอรีต่อไปนี้บนดิสก์:
ในขณะที่ตัววนซ้ำที่มีลำดับเชิงเส้นจะเคลื่อนที่ผ่านโฟลเดอร์และไฟล์ระดับบนสุด (รายการไดเร็กทอรีเดียว) ตัววนซ้ำแบบวนซ้ำจะข้ามผ่านโฟลเดอร์ย่อยเช่นกันและแสดงรายการโฟลเดอร์และไฟล์ทั้งหมด (รายการไดเร็กทอรีพร้อมรายการไดเร็กทอรีย่อย):
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
ที่สองเป็นไฟล์IteratorIterator
. สิ่งนี้แสดงให้เห็นถึงความยืดหยุ่นที่ตัวทำซ้ำมี: คุณสามารถแทนที่ด้วยกันได้โค้ดที่อยู่ภายในforeach
จะยังคงทำงานตามที่คาดไว้
มาเริ่มรับรายชื่อทั้งหมดรวมถึงไดเรกทอรีย่อย
เนื่องจากตอนนี้เราได้ระบุประเภทของการทำซ้ำแล้วให้พิจารณาเปลี่ยนเป็นการทำซ้ำประเภทอื่น
เรารู้ว่าเราต้องสำรวจต้นไม้ทั้งต้นไม่ใช่แค่ระดับแรกเท่านั้น ให้มีการทำงานที่เรียบง่ายกับforeach
เราต้องมีชนิดที่แตกต่างกันของ RecursiveIteratorIterator
iterator: และสามารถทำซ้ำได้เฉพาะวัตถุคอนเทนเนอร์ที่มีRecursiveIterator
อินเทอร์เฟซเท่านั้น
อินเทอร์เฟซเป็นสัญญา คลาสใด ๆ ที่ใช้งานได้สามารถใช้ร่วมกับRecursiveIteratorIterator
. ตัวอย่างของชั้นดังกล่าวเป็นซึ่งเป็นสิ่งที่ต้องการที่แตกต่างของRecursiveDirectoryIterator
recursiveDirectoryIterator
มาดูตัวอย่างโค้ดแรกก่อนที่จะเขียนประโยคอื่นด้วย I-word:
$dir = new RecursiveDirectoryIterator($path);
echo "[$path]\n";
foreach ($dir as $file) {
echo " ├ $file\n";
}
ตัวอย่างที่สามนี้เกือบจะเหมือนกันกับตัวอย่างแรก แต่จะสร้างผลลัพธ์ที่แตกต่างกัน:
[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
แน่ใจว่านี้ไม่ชนะการประกวดความงาม แต่มันแสดงให้เห็นว่ามีการ iterator recursive มีข้อมูลมากขึ้นกว่าเพียงแค่การสั่งการเชิงเส้นของคีย์และคุ้มค่า แม้จะ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
(ไม่มีตัวอย่าง): แสดงรายการไฟล์ในไดเร็กทอรีย่อยก่อนจากนั้นไดเร็กทอรีผลลัพธ์ของตัวอย่างที่ 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
เมื่อคุณเปรียบเทียบกับการส่งผ่านมาตรฐานสิ่งเหล่านี้ทั้งหมดจะไม่มีให้ การวนซ้ำแบบวนซ้ำจึงซับซ้อนกว่าเล็กน้อยเมื่อคุณต้องพันศีรษะของคุณรอบ ๆ อย่างไรก็ตามมันใช้งานง่ายเพราะมันทำงานเหมือนกับตัววนซ้ำคุณใส่ลงใน a foreach
และทำเสร็จแล้ว
ฉันคิดว่านี่เป็นตัวอย่างเพียงพอสำหรับคำตอบเดียว คุณสามารถค้นหาซอร์สโค้ดแบบเต็มรวมทั้งตัวอย่างในการแสดงต้นไม้ ascii ที่ดูดีได้ในส่วนสำคัญนี้: https://gist.github.com/3599532
ทำด้วยตัวเอง:สร้าง
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
เนื่องจากสิ่งนี้เหมือนกันกับประเภทอื่น ๆ แต่ฉันได้ให้ข้อมูลทางเทคนิคเกี่ยวกับวิธีการทำงานจริง ตัวอย่างที่ฉันคิดว่าแสดงให้เห็นถึงความแตกต่าง: ประเภทของการวนซ้ำเป็นความแตกต่างหลักระหว่างสองสิ่งนี้ ไม่รู้ว่าถ้าคุณซื้อประเภทของการทำซ้ำคุณจะให้เหรียญแตกต่างกันเล็กน้อย แต่ IMHO ไม่ง่ายเลยที่จะมีการทำซ้ำประเภทความหมาย
อะไรคือความแตกต่างของ
IteratorIterator
และRecursiveIteratorIterator
?
เพื่อให้เข้าใจถึงความแตกต่างระหว่างตัวทำซ้ำสองตัวนี้ก่อนอื่นเราต้องเข้าใจหลักการตั้งชื่อที่ใช้และความหมายของตัววนซ้ำ
PHP มีตัววนซ้ำที่ไม่ "เรียกซ้ำ" เช่นArrayIterator
และFilesystemIterator
. นอกจากนี้ยังมีตัวทำซ้ำแบบ "เรียกซ้ำ" เช่นRecursiveArrayIterator
และRecursiveDirectoryIterator
. อย่างหลังมีวิธีการที่ทำให้สามารถเจาะลงไปได้ แต่เดิมทำไม่ได้
เมื่ออินสแตนซ์ของตัววนซ้ำเหล่านี้ถูกวนซ้ำด้วยตัวเองแม้กระทั่งตัววนซ้ำค่าจะมาจากระดับ "บนสุด" เท่านั้นแม้ว่าจะวนลูปเหนืออาร์เรย์ที่ซ้อนกันหรือไดเร็กทอรีที่มีไดเร็กทอรีย่อยก็ตาม
iterators recursive ใช้พฤติกรรม recursive (ผ่านhasChildren()
, getChildren()
) แต่ไม่ได้ใช้ประโยชน์จากมัน
มันอาจจะดีกว่าถ้าคิดว่าตัววนซ้ำแบบวนซ้ำเป็นตัวทำซ้ำแบบ "เรียกซ้ำ" พวกมันมีความสามารถในการวนซ้ำแบบวนซ้ำ แต่เพียงแค่วนซ้ำในอินสแตนซ์ของคลาสใดคลาสหนึ่งเหล่านี้จะไม่ทำเช่นนั้น หากต้องการใช้ประโยชน์จากพฤติกรรมที่เกิดซ้ำให้อ่านต่อไป
นี่คือที่RecursiveIteratorIterator
มาในการเล่น มันมีความรู้เกี่ยวกับวิธีการเรียกตัววนซ้ำแบบ "เรียกซ้ำ" ในลักษณะที่จะเจาะลึกลงไปในโครงสร้างในแบบปกติแบนลูป ทำให้พฤติกรรมที่เกิดขึ้นซ้ำ ๆ เป็นการกระทำ โดยพื้นฐานแล้วเป็นการทำงานในการก้าวข้ามค่าแต่ละค่าในตัววนซ้ำโดยมองหาว่ามี "เด็ก" ที่จะฟื้นขึ้นมาอีกหรือไม่และก้าวเข้าและออกจากคอลเลกชันของเด็กเหล่านั้น คุณติดอินสแตนซ์RecursiveIteratorIterator
ไว้ใน foreach แล้วมันก็ดำดิ่งลงไปในโครงสร้างเพื่อที่คุณจะได้ไม่ต้องทำ
ถ้าRecursiveIteratorIterator
ไม่ได้ใช้คุณจะต้องเขียนลูป recursive ของคุณเองเพื่อใช้ประโยชน์จากพฤติกรรมการเวียนเกิดการตรวจสอบกับ "recursible" iterator เป็นและการใช้hasChildren()
getChildren()
เพื่อให้เป็นช่วงสั้น ๆภาพรวมของRecursiveIteratorIterator
วิธีการที่แตกต่างจากIteratorIterator
? โดยพื้นฐานแล้วคุณกำลังถามคำถามประเภทเดียวกันว่าลูกแมวกับต้นไม้ต่างกันอย่างไร? เพียงเพราะทั้งสองอย่างปรากฏในสารานุกรมเดียวกัน (หรือคู่มือสำหรับผู้วนซ้ำ) ไม่ได้หมายความว่าคุณควรสับสนระหว่างทั้งสอง
หน้าที่ของวัตถุIteratorIterator
คือการใช้Traversable
วัตถุใด ๆและรวมเข้าด้วยกันเพื่อให้เป็นไปตามIterator
อินเทอร์เฟซ การใช้สำหรับสิ่งนี้คือเพื่อให้สามารถใช้ลักษณะการทำงานเฉพาะของตัววนซ้ำกับวัตถุที่ไม่ใช่ตัววนซ้ำ
เพื่อให้เป็นตัวอย่างที่ใช้ได้จริงDatePeriod
ชั้นเรียนนี้เป็นTraversable
แต่ไม่ใช่Iterator
ไฟล์. ด้วยเหตุนี้เราจึงสามารถวนซ้ำค่าของมันด้วยforeach()
แต่ไม่สามารถทำสิ่งอื่น ๆ ที่เราทำโดยปกติด้วยตัววนซ้ำเช่นการกรอง
TASK : วนรอบวันจันทร์วันพุธและวันศุกร์ของสี่สัปดาห์ถัดไป
ใช่นี่เป็นเรื่องเล็กน้อยโดยการforeach
ข้าม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(…));
ในขณะที่คุณสามารถดูนี้มีอะไรใด ๆ จะทำอย่างไรกับการวนไปเรียนหรือการเรียกซ้ำ iterator และอยู่ในนั้นความแตกต่างระหว่างและIteratorIterator
RecursiveIteratorIterator
RecursiveIteraratorIterator
มีไว้สำหรับการวนซ้ำบนตัววนซ้ำRecursiveIterator
("เรียกซ้ำ") โดยใช้ประโยชน์จากพฤติกรรมการเรียกซ้ำที่พร้อมใช้งาน
IteratorIterator
มีไว้สำหรับการใช้Iterator
พฤติกรรมกับTraversable
อ็อบเจ็กต์ที่ไม่ใช่ตัววนซ้ำ
IteratorIterator
แค่ประเภทมาตรฐานของการส่งผ่านคำสั่งเชิงเส้นสำหรับTraversable
วัตถุใช่หรือไม่? สิ่งที่สามารถใช้งานได้โดยไม่ต้องใช้มันforeach
? และยิ่งไม่ได้เป็นRecursiveIterator
เสมอTraversable
และดังนั้นจึงไม่เพียงIteratorIterator
แต่ยังRecursiveIteratorIterator
เสมอ"สำหรับการใช้Iterator
พฤติกรรมที่ไม่ iterator, ทะลุวัตถุ" ? (ตอนนี้ฉันจะบอกว่าforeach
ใช้ประเภทของการวนซ้ำผ่านอ็อบเจ็กต์ตัววนซ้ำบนอ็อบเจ็กต์คอนเทนเนอร์ที่ใช้อินเทอร์เฟซประเภทตัววนซ้ำดังนั้นนี่คือตัววนซ้ำคอนเทนเนอร์ - อ็อบเจกต์เสมอTraversable
)
IteratorIterator
เป็นคลาสที่เกี่ยวกับการห่อTraversable
วัตถุในIterator
ไฟล์. ไม่มีอะไรมาก . ดูเหมือนคุณจะใช้คำนี้โดยทั่วไปมากกว่า
Recursive
ในหมายถึงพฤติกรรมในขณะที่ชื่อที่เหมาะสมมากขึ้นจะได้รับหนึ่งซึ่งอธิบายความสามารถเช่น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