Làm thế nào để RecursiveIteratorIterator
làm việc?
Hướng dẫn sử dụng PHP không có nhiều tài liệu hoặc giải thích. Sự khác biệt giữa IteratorIterator
và là RecursiveIteratorIterator
gì?
Làm thế nào để RecursiveIteratorIterator
làm việc?
Hướng dẫn sử dụng PHP không có nhiều tài liệu hoặc giải thích. Sự khác biệt giữa IteratorIterator
và là RecursiveIteratorIterator
gì?
RecursiveIteratorIterator
hoạt động, bạn đã hiểu cách thức IteratorIterator
hoạt động chưa? Ý tôi là về cơ bản nó giống nhau, chỉ khác là giao diện được tiêu dùng bởi cả hai là khác nhau. Và bạn có quan tâm hơn đến một số ví dụ hay bạn muốn xem sự khác biệt của việc triển khai mã C cơ bản?
IteratorIterator
bản đồ Iterator
và IteratorAggregate
thành một Iterator
, nơi REcusiveIteratorIterator
được sử dụng để đi qua tái phạm aRecursiveIterator
Câu trả lời:
RecursiveIteratorIterator
là một Iterator
thực hiện cụ thể đi ngang qua cây . Nó cho phép lập trình viên duyệt qua một đối tượng vùng chứa triển khai RecursiveIterator
giao diện, hãy xem Iterator trong Wikipedia để biết các nguyên tắc chung, kiểu, ngữ nghĩa và mẫu của trình vòng lặp.
Khác với đối tượng thực hiện IteratorIterator
cụ Iterator
thể truyền ngang theo thứ tự tuyến tính (và mặc định chấp nhận bất kỳ loại nào Traversable
trong hàm tạo của nó), hàm RecursiveIteratorIterator
cho phép lặp qua tất cả các nút trong một cây có thứ tự các đối tượng và hàm tạo của nó nhận a RecursiveIterator
.
Tóm lại: RecursiveIteratorIterator
cho phép bạn lặp qua một cây, IteratorIterator
cho phép bạn lặp qua một danh sách. Tôi sẽ sớm hiển thị điều đó với một số ví dụ mã bên dưới.
Về mặt kỹ thuật, điều này hoạt động bằng cách vượt ra ngoài tuyến tính bằng cách duyệt qua tất cả các nút con (nếu có). Điều này là có thể vì theo định nghĩa tất cả các nút con của một nút lại là a RecursiveIterator
. Sau Iterator
đó, cấp trên sẽ xếp chồng các RecursiveIterator
s khác nhau theo độ sâu của chúng và giữ một con trỏ đến cấp phụ đang hoạt động hiện tại Iterator
để duyệt.
Điều này cho phép truy cập tất cả các nút của một cây.
Các nguyên tắc cơ bản giống như với IteratorIterator
: Một giao diện chỉ định kiểu lặp và lớp trình lặp cơ sở là việc triển khai các ngữ nghĩa này. So sánh với các ví dụ bên dưới, đối với vòng lặp tuyến tính, foreach
bạn thường không nghĩ nhiều về chi tiết triển khai trừ khi bạn cần xác định một cái mới Iterator
(ví dụ khi bản thân một số kiểu cụ thể không thực hiện Traversable
).
Đối với Traversal
phép lặp truyền tải đệ quy - trừ khi bạn không sử dụng phép lặp được xác định trước mà đã có phép lặp truyền tải đệ quy - thì thông thường bạn cần phải khởi tạo RecursiveIteratorIterator
lần lặp hiện có hoặc thậm chí viết một phép lặp truyền tải đệ quy Traversable
của riêng bạn để có kiểu lặp truyền tải này foreach
.
Mẹo: Có thể bạn đã không triển khai cái này hay cái kia của riêng bạn, vì vậy đây có thể là điều đáng làm để bạn có kinh nghiệm thực tế về sự khác biệt mà chúng có. Bạn tìm thấy một gợi ý DIY ở cuối câu trả lời.
Nói tóm lại, sự khác biệt về kỹ thuật:
IteratorIterator
lấy bất kỳ Traversable
cho truyền tuyến tính, RecursiveIteratorIterator
cần một RecursiveIterator
vòng lặp cụ thể hơn trên cây.IteratorIterator
hiển thị chính của nó Iterator
thông qua getInnerIerator()
, RecursiveIteratorIterator
cung cấp Iterator
chỉ phụ hoạt động hiện tại thông qua phương thức đó.IteratorIterator
hoàn toàn không nhận thức được bất cứ điều gì như cha mẹ hoặc con cái, nhưng cũng RecursiveIteratorIterator
biết cách đưa và đón con cái.IteratorIterator
không cần một ngăn xếp các trình vòng lặp, RecursiveIteratorIterator
có một ngăn xếp như vậy và biết trình vòng lặp con đang hoạt động.IteratorIterator
có thứ tự của nó do tính tuyến tính và không có sự lựa chọn, RecursiveIteratorIterator
có sự lựa chọn để truyền tải xa hơn và cần phải quyết định cho mỗi nút (quyết định thông qua chế độ mỗiRecursiveIteratorIterator
).RecursiveIteratorIterator
có nhiều phương pháp hơn IteratorIterator
.Tóm lại: RecursiveIterator
là một kiểu lặp cụ thể (lặp qua cây) hoạt động trên các trình vòng lặp của chính nó, cụ thể là RecursiveIterator
. Đó là nguyên tắc cơ bản giống như với IteratorIerator
, nhưng kiểu lặp lại khác (thứ tự tuyến tính).
Lý tưởng nhất là bạn cũng có thể tạo bộ của riêng mình. Điều duy nhất cần thiết là trình lặp của bạn triển khai Traversable
có thể thực hiện được thông qua Iterator
hoặc IteratorAggregate
. Sau đó, bạn có thể sử dụng nó với foreach
. Ví dụ: một số loại đối tượng lặp đệ quy duyệt qua cây bậc ba cùng với giao diện lặp theo cho (các) đối tượng vùng chứa.
Hãy cùng xem lại một số ví dụ thực tế không hề trừu tượng. Giữa các giao diện, trình vòng lặp cụ thể, đối tượng vùng chứa và ngữ nghĩa lặp, điều này có thể không phải là một ý tưởng tồi.
Lấy một danh sách thư mục làm ví dụ. Hãy xem xét bạn đã có tệp và cây thư mục sau trên đĩa:
Trong khi một trình vòng lặp có thứ tự tuyến tính chỉ đi qua thư mục cấp cao nhất và các tệp (một danh sách thư mục duy nhất), thì trình lặp đệ quy cũng đi qua các thư mục con và liệt kê tất cả các thư mục và tệp (danh sách thư mục với danh sách các thư mục con của nó):
Non-Recursive Recursive
============= =========
[tree] [tree]
├ dirA ├ dirA
└ fileA │ ├ dirB
│ │ └ fileD
│ ├ fileB
│ └ fileC
└ fileA
Bạn có thể dễ dàng so sánh cái này với IteratorIterator
cái không có đệ quy để duyệt qua cây thư mục. Và RecursiveIteratorIterator
cái có thể đi qua cây như danh sách Đệ quy hiển thị.
Tại một ví dụ rất cơ bản đầu tiên với một DirectoryIterator
mà cụ Traversable
cho phép foreach
để lặp qua nó:
$path = 'tree';
$dir = new DirectoryIterator($path);
echo "[$path]\n";
foreach ($dir as $file) {
echo " ├ $file\n";
}
Đầu ra mẫu cho cấu trúc thư mục ở trên sau đó là:
[tree]
├ .
├ ..
├ dirA
├ fileA
Như bạn thấy đây là chưa sử dụng IteratorIterator
hoặc RecursiveIteratorIterator
. Thay vào đó, nó chỉ sử dụng foreach
nó hoạt động trênTraversable
giao diện.
Vì foreach
theo mặc định chỉ biết kiểu lặp có tên là thứ tự tuyến tính, chúng ta có thể muốn chỉ định kiểu lặp một cách rõ ràng. Thoạt nhìn, nó có vẻ quá dài dòng, nhưng với mục đích trình diễn (và để tạo sự khác biệt với việc RecursiveIteratorIterator
hiển thị rõ ràng hơn sau này), hãy chỉ định kiểu lặp tuyến tính xác định rõ ràng IteratorIterator
kiểu lặp cho danh sách thư mục:
$files = new IteratorIterator($dir);
echo "[$path]\n";
foreach ($files as $file) {
echo " ├ $file\n";
}
Ví dụ này gần giống với ví dụ đầu tiên, sự khác biệt $files
là bây giờ là một IteratorIterator
kiểu lặp lại cho Traversable
$dir
:
$files = new IteratorIterator($dir);
Như thường lệ, hành động lặp được thực hiện bởi foreach
:
foreach ($files as $file) {
Đầu ra hoàn toàn giống nhau. Vậy khác nhau ở điểm nào? Khác nhau là đối tượng được sử dụng trong foreach
. Trong ví dụ đầu tiên, nó là một DirectoryIterator
trong ví dụ thứ hai, nó là IteratorIterator
. Điều này cho thấy các trình vòng lặp linh hoạt có: Bạn có thể thay thế chúng bằng nhau, mã bên trongforeach
chỉ tiếp tục hoạt động như mong đợi.
Hãy bắt đầu lấy toàn bộ danh sách, bao gồm cả các thư mục con.
Vì bây giờ chúng ta đã chỉ định kiểu lặp, hãy xem xét thay đổi nó thành một kiểu lặp khác.
Chúng tôi biết chúng tôi cần phải đi qua toàn bộ cây ngay bây giờ, không chỉ cấp độ đầu tiên. Để có mà làm việc với một đơn giản foreach
chúng ta cần một loại khác nhau của iterator: RecursiveIteratorIterator
. Và cái đó chỉ có thể lặp qua các đối tượng vùng chứa có RecursiveIterator
giao diện .
Giao diện là một hợp đồng. Bất kỳ lớp nào triển khai nó đều có thể được sử dụng cùng với RecursiveIteratorIterator
. Một ví dụ về một lớp như vậy là RecursiveDirectoryIterator
, giống như biến thể đệ quy củaDirectoryIterator
.
Hãy xem ví dụ về mã đầu tiên trước khi viết bất kỳ câu nào khác với I-word:
$dir = new RecursiveDirectoryIterator($path);
echo "[$path]\n";
foreach ($dir as $file) {
echo " ├ $file\n";
}
Ví dụ thứ ba này gần giống với ví dụ đầu tiên, tuy nhiên nó tạo ra một số đầu ra khác nhau:
[tree]
├ tree\.
├ tree\..
├ tree\dirA
├ tree\fileA
Được rồi, không có gì khác biệt, tên tệp bây giờ chứa tên đường dẫn phía trước, nhưng phần còn lại trông cũng tương tự.
Như ví dụ cho thấy, ngay cả đối tượng thư mục đã triển khai RecursiveIterator
giao diện, điều này vẫn chưa đủ để thực hiện việc duyệt foreach
qua toàn bộ cây thư mục. Đây là nơi RecursiveIteratorIterator
bắt đầu hoạt động. Ví dụ 4 cho thấy cách:
$files = new RecursiveIteratorIterator($dir);
echo "[$path]\n";
foreach ($files as $file) {
echo " ├ $file\n";
}
Việc sử dụng RecursiveIteratorIterator
thay vì chỉ $dir
đối tượng trước đó sẽ thực hiện foreach
để duyệt qua tất cả các tệp và thư mục theo cách đệ quy. Sau đó, điều này sẽ liệt kê tất cả các tệp, vì kiểu lặp đối tượng đã được chỉ định ngay bây giờ:
[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
Điều này đã chứng minh sự khác biệt giữa đường ngang và đường ngang cây. Có RecursiveIteratorIterator
thể duyệt qua bất kỳ cấu trúc dạng cây nào dưới dạng danh sách các phần tử. Vì có nhiều thông tin hơn (như mức độ lặp đang diễn ra hiện tại), có thể truy cập đối tượng trình lặp trong khi lặp qua nó và ví dụ như thụt lề đầu ra:
echo "[$path]\n";
foreach ($files as $file) {
$indent = str_repeat(' ', $files->getDepth());
echo $indent, " ├ $file\n";
}
Và đầu ra của Ví dụ 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
Chắc chắn điều này không giành chiến thắng trong một cuộc thi sắc đẹp, nhưng nó cho thấy rằng với trình lặp đệ quy, có nhiều thông tin hơn chỉ là thứ tự tuyến tính của khóa và giá trị . Cũngforeach
chỉ có thể thể hiện loại tuyến tính này, việc truy cập trình lặp tự nó cho phép lấy thêm thông tin.
Tương tự như siêu thông tin, cũng có nhiều cách khác nhau có thể để duyệt qua cây và do đó sắp xếp đầu ra. Đây là Chế độ củaRecursiveIteratorIterator
và nó có thể được thiết lập với hàm tạo.
Ví dụ tiếp theo sẽ yêu RecursiveDirectoryIterator
cầu xóa các mục chấm ( .
và ..
) vì chúng tôi không cần chúng. Nhưng chế độ đệ quy cũng sẽ được thay đổi để lấy phần tử cha (thư mục con) đầu tiên ( SELF_FIRST
) trước phần tử con (tệp và con con trong thư mục con):
$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";
}
Đầu ra bây giờ hiển thị các mục nhập thư mục con được liệt kê chính xác, nếu bạn so sánh với đầu ra trước đó, chúng không có ở đó:
[tree]
├ tree\dirA
├ tree\dirA\dirB
├ tree\dirA\dirB\fileD
├ tree\dirA\fileB
├ tree\dirA\fileC
├ tree\fileA
Do đó, chế độ đệ quy kiểm soát những gì và khi nào một nhánh hoặc lá trong cây được trả về, đối với ví dụ thư mục:
LEAVES_ONLY
(mặc định): Chỉ danh sách các tệp, không có thư mục.SELF_FIRST
(ở trên): Liệt kê thư mục và sau đó là các tệp trong đó.CHILD_FIRST
(w / o example): Liệt kê các tệp trong thư mục con trước, sau đó đến thư mục.Đầu ra của Ví dụ 5 với hai chế độ khác:
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
Khi bạn so sánh điều đó với truyền tải tiêu chuẩn, tất cả những điều này không có sẵn. Do đó, lặp đệ quy sẽ phức tạp hơn một chút khi bạn cần phải quấn quanh nó, tuy nhiên nó rất dễ sử dụng vì nó hoạt động giống như một trình lặp, bạn đặt nó vào mộtforeach
và xong.
Tôi nghĩ đây là những ví dụ đủ cho một câu trả lời. Bạn có thể tìm thấy mã nguồn đầy đủ cũng như một ví dụ để hiển thị cây ascii đẹp mắt trong ý chính này: https://gist.github.com/3599532
Do It Yourself: Thực hiện
RecursiveTreeIterator
Công việc Theo Dòng.
Ví dụ 5 đã chứng minh rằng có siêu thông tin về trạng thái của trình vòng lặp. Tuy nhiên, điều này đã cố tình chứng minh trong các foreach
lần lặp. Trong cuộc sống thực, điều này tự nhiên thuộc về bên trong RecursiveIterator
.
Một ví dụ tốt hơn là RecursiveTreeIterator
, nó quan tâm đến việc thụt lề, tiền tố, v.v. Xem đoạn mã sau:
$dir = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
$lines = new RecursiveTreeIterator($dir);
$unicodeTreePrefix($lines);
echo "[$path]\n", implode("\n", iterator_to_array($lines));
Mục RecursiveTreeIterator
đích là để làm việc theo từng dòng, đầu ra khá dễ dàng với một vấn đề nhỏ:
[tree]
├ tree\dirA
│ ├ tree\dirA\dirB
│ │ └ tree\dirA\dirB\fileD
│ ├ tree\dirA\fileB
│ └ tree\dirA\fileC
└ tree\fileA
Khi được sử dụng kết hợp với một, RecursiveDirectoryIterator
nó hiển thị toàn bộ tên đường dẫn chứ không chỉ tên tệp. Phần còn lại có vẻ tốt. Điều này là do tên tệp được tạo bởi SplFileInfo
. Thay vào đó, chúng sẽ được hiển thị dưới dạng tên cơ sở. Đầu ra mong muốn như sau:
/// Solved ///
[tree]
├ dirA
│ ├ dirB
│ │ └ fileD
│ ├ fileB
│ └ fileC
└ fileA
Tạo một lớp decorator có thể được sử dụng RecursiveTreeIterator
thay vì RecursiveDirectoryIterator
. Nó phải cung cấp tên cơ sở của hiện tại SplFileInfo
thay vì tên đường dẫn. Đoạn mã cuối cùng sau đó có thể trông giống như sau:
$lines = new RecursiveTreeIterator(
new DiyRecursiveDecorator($dir)
);
$unicodeTreePrefix($lines);
echo "[$path]\n", implode("\n", iterator_to_array($lines));
Những đoạn này bao gồm $unicodeTreePrefix
một phần của ý chính trong Phụ lục: Do It Yourself: Make the RecursiveTreeIterator
Work Line by Line. .
RecursiveIteratorIterator
bởi vì điều này phổ biến với các loại khác nhưng tôi đã đưa ra một số thông tin kỹ thuật về cách nó thực sự hoạt động. Các ví dụ tôi nghĩ cho thấy rõ sự khác biệt: kiểu lặp là sự khác biệt chính giữa hai loại. Không có ý tưởng nếu bạn mua loại lặp lại mà bạn nghĩ nó hơi khác một chút nhưng IMHO không dễ dàng với các loại lặp có ngữ nghĩa.
Sự khác biệt của
IteratorIterator
và làRecursiveIteratorIterator
gì?
Để hiểu sự khác biệt giữa hai trình vòng lặp này, trước tiên người ta phải hiểu một chút về các quy ước đặt tên được sử dụng và ý nghĩa của chúng tôi đối với trình vòng lặp "đệ quy".
PHP có các trình vòng lặp không phải "đệ quy", chẳng hạn như ArrayIterator
và FilesystemIterator
. Ngoài ra còn có các trình vòng lặp "đệ quy" như RecursiveArrayIterator
và RecursiveDirectoryIterator
. Loại thứ hai có các phương pháp cho phép chúng được đi sâu vào, phương pháp trước thì không.
Khi các bản sao của các trình vòng lặp này được lặp lại một mình, ngay cả các trình lặp đệ quy, các giá trị chỉ đến từ mức "cao nhất" ngay cả khi lặp qua một mảng hoặc thư mục lồng nhau với các thư mục con.
Các trình lặp đệ quy thực hiện hành vi đệ quy (qua hasChildren()
, getChildren()
) nhưng không khai thác nó.
Có thể tốt hơn nếu nghĩ về các trình vòng lặp đệ quy là các trình vòng lặp "đệ quy", chúng có khả năng được lặp lại một cách đệ quy nhưng chỉ cần lặp qua một phiên bản của một trong các lớp này sẽ không làm được điều đó. Để khai thác hành vi đệ quy, hãy tiếp tục đọc.
Đây là nơi RecursiveIteratorIterator
để chơi. Nó có kiến thức về cách gọi các trình vòng lặp "đệ quy" theo cách để đi sâu vào cấu trúc trong một vòng lặp bình thường, phẳng. Nó đưa hành vi đệ quy vào hoạt động. Về cơ bản, nó thực hiện công việc lướt qua từng giá trị trong trình lặp, tìm kiếm xem có "con" để đệ quy vào hay không, và bước vào và ra khỏi các tập hợp con đó. Bạn gắn một thể hiện của RecursiveIteratorIterator
vào foreach và nó đi sâu vào cấu trúc để bạn không cần phải làm như vậy.
Nếu RecursiveIteratorIterator
không được sử dụng, bạn sẽ phải viết các vòng lặp đệ quy của riêng mình để khai thác hành vi đệ quy, kiểm tra xem hasChildren()
và sử dụng trình lặp "đệ quy" getChildren()
.
Vì vậy, đó là một tổng quan ngắn gọn về RecursiveIteratorIterator
, nó khác nhau như thế nào IteratorIterator
? Về cơ bản, bạn đang hỏi cùng một loại câu hỏi như Sự khác biệt giữa mèo con và một cái cây là gì? Chỉ vì cả hai đều xuất hiện trong cùng một bách khoa toàn thư (hoặc sách hướng dẫn, dành cho trình duyệt) không có nghĩa là bạn nên nhầm lẫn giữa hai.
Công việc của IteratorIterator
là lấy bất kỳ Traversable
đối tượng nào và bọc nó sao cho nó thỏa mãn Iterator
giao diện. Việc sử dụng cho điều này là sau đó có thể áp dụng hành vi dành riêng cho trình lặp trên đối tượng không phải trình lặp.
Để đưa ra một ví dụ thực tế, DatePeriod
lớp học Traversable
nhưng không phải là một Iterator
. Như vậy, chúng ta có thể lặp lại các giá trị của nó foreach()
nhưng không thể làm những việc khác mà chúng ta thường làm với một trình lặp, chẳng hạn như lọc.
NHIỆM VỤ : Lặp lại các ngày Thứ Hai, Thứ Tư và Thứ Sáu của bốn tuần tiếp theo.
Có, điều này là nhỏ bằng cách foreach
-ing qua DatePeriod
và sử dụng if()
trong vòng lặp; nhưng đó không phải là điểm của ví dụ này!
$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) { … }
Đoạn mã trên sẽ không hoạt động bởi vì đoạn mã CallbackFilterIterator
mong đợi một phiên bản của một lớp triển khai Iterator
giao diện, mà DatePeriod
không. Tuy nhiên, vì nó là Traversable
chúng ta có thể dễ dàng đáp ứng yêu cầu đó bằng cách sử dụng IteratorIterator
.
$period = new IteratorIterator(new DatePeriod(…));
Như bạn có thể thấy, điều này không liên quan gì đến việc lặp qua các lớp trình lặp cũng như đệ quy, và ở đó sự khác biệt giữa IteratorIterator
và RecursiveIteratorIterator
.
RecursiveIteraratorIterator
là để lặp qua một RecursiveIterator
(trình lặp "đệ quy"), khai thác hành vi đệ quy có sẵn.
IteratorIterator
là để áp dụng Iterator
hành vi cho các Traversable
đối tượng , không phải trình lặp .
IteratorIterator
chỉ là loại tiêu chuẩn của trình tự tuyến tính duyệt cho Traversable
các đối tượng? Những người có thể được sử dụng mà không có nó chỉ foreach
như vậy? Và thậm chí xa hơn, không phải RecursiveIterator
luôn luôn a Traversable
và do đó không chỉ IteratorIterator
mà còn RecursiveIteratorIterator
luôn luôn là "để áp dụng Iterator
hành vi cho các đối tượng không lặp lại, có thể truy cập" ? (Bây giờ tôi muốn nói foreach
áp dụng kiểu lặp qua các đối tượng iterator trên chứa các đối tượng mà thực hiện một giao diện iterator kiểu vì vậy đây là iterator-container đối tượng, luôn luôn Traversable
)
IteratorIterator
là một lớp là tất cả về việc gói Traversable
các đối tượng trong một Iterator
. Không có gì hơn . Có vẻ như bạn đang áp dụng thuật ngữ này một cách tổng quát hơn.
Recursive
trong RecursiveIterator
ngụ ý hành vi, trong khi một cái tên phù hợp hơn sẽ là một cái tên mô tả khả năng, chẳng hạn RecursibleIterator
.
Khi được sử dụng với iterator_to_array()
, RecursiveIteratorIterator
sẽ đệ quy đi bộ mảng để tìm tất cả các giá trị. Có nghĩa là nó sẽ làm phẳng mảng ban đầu.
IteratorIterator
sẽ giữ nguyên cấu trúc phân cấp ban đầu.
Ví dụ này sẽ cho bạn thấy rõ sự khác biệt:
$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))
tương đương với new ArrayIterator($array)
, nghĩa là, bên ngoài IteratorIterator
không làm gì cả. Hơn nữa, việc làm phẳng đầu ra không liên quan gì iterator_to_array
- nó chỉ chuyển đổi trình lặp thành một mảng. Làm phẳng là một thuộc tính của cách RecursiveArrayIterator
đi bộ lặp bên trong của nó.
RecursiveDirectoryIterator nó hiển thị toàn bộ tên đường dẫn chứ không chỉ tên tệp. Phần còn lại có vẻ tốt. Điều này là do tên tệp được tạo bởi SplFileInfo. Thay vào đó, chúng sẽ được hiển thị dưới dạng tên cơ sở. Đầu ra mong muốn như sau:
$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";
}
}
đầu ra:
tree
├ dirA
│ ├ dirB
│ │ └ fileD
│ ├ fileB
│ └ fileC
└ fileA