RecursiveIteratorIterator hoạt động như thế nào trong PHP?


88

Làm thế nào để RecursiveIteratorIteratorlà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 IteratorIteratorvà là RecursiveIteratorIteratorgì?


2
có một ví dụ tại php.net/manual/en/recursiveiteratoriterator.construct.php và cũng có một Giới thiệu tại php.net/manual/en/class.iteratoriterator.php - bạn có thể vui lòng chỉ ra chính xác những gì bạn khó hiểu không . Hướng dẫn sử dụng cần có những nội dung gì để dễ nắm bắt hơn?
Gordon

1
Nếu bạn hỏi cách thức RecursiveIteratorIteratorhoạt động, bạn đã hiểu cách thức IteratorIteratorhoạ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?
hakre

@Gordon tôi đã không chắc chắn làm thế nào để vòng lặp foreach duy nhất có thể đi qua tất cả các yếu tố trong cấu trúc cây
varuog

@hakra Tôi hiện đang cố gắng nghiên cứu tất cả các giao diện tích hợp sẵn cũng như giao diện spl và triển khai trình lặp. Tôi muốn biết nó hoạt động như thế nào trong nền với forach loop với một số ví dụ.
varuog

@hakre Cả hai đều rất khác nhau. IteratorIteratorbản đồ IteratorIteratorAggregatethành một Iterator, nơi REcusiveIteratorIteratorđược sử dụng để đi qua tái phạm aRecursiveIterator
Adam

Câu trả lời:


249

RecursiveIteratorIteratorlà một Iteratorthự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 RecursiveIteratorgiao 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 IteratorIteratorcụ Iteratorthể 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 Traversabletrong hàm tạo của nó), hàm RecursiveIteratorIteratorcho 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: RecursiveIteratorIteratorcho phép bạn lặp qua một cây, IteratorIteratorcho 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 RecursiveIterators 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, foreachbạ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 Traversalphé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 RecursiveIteratorIteratorlần lặp hiện có hoặc thậm chí viết một phép lặp truyền tải đệ quy Traversablecủ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:

  • Trong khi IteratorIteratorlấy bất kỳ Traversablecho truyền tuyến tính, RecursiveIteratorIteratorcần một RecursiveIteratorvòng lặp cụ thể hơn trên cây.
  • Nơi IteratorIteratorhiển thị chính của nó Iteratorthông qua getInnerIerator(), RecursiveIteratorIteratorcung cấp Iteratorchỉ phụ hoạt động hiện tại thông qua phương thức đó.
  • Mặc dù IteratorIteratorhoà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 RecursiveIteratorIteratorbiết cách đưa và đón con cái.
  • IteratorIteratorkhông cần một ngăn xếp các trình vòng lặp, RecursiveIteratorIteratorcó một ngăn xếp như vậy và biết trình vòng lặp con đang hoạt động.
  • Nơi IteratorIteratorcó thứ tự của nó do tính tuyến tính và không có sự lựa chọn, RecursiveIteratorIteratorcó 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 ).
  • RecursiveIteratorIteratorcó nhiều phương pháp hơn IteratorIterator.

Tóm lại: RecursiveIteratorlà 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 Traversablecó thể thực hiện được thông qua Iteratorhoặ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:

Cây thư mục

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 IteratorIteratorcái không có đệ quy để duyệt qua cây thư mục. Và RecursiveIteratorIteratorcá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 DirectoryIteratormà cụ Traversablecho 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 IteratorIteratorhoặc RecursiveIteratorIterator. Thay vào đó, nó chỉ sử dụng foreachnó hoạt động trênTraversable giao diện.

foreachtheo 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 RecursiveIteratorIteratorhiể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 IteratorIteratorkiể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 $fileslà bây giờ là một IteratorIteratorkiể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 DirectoryIteratortrong 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 foreachchú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ó RecursiveIteratorgiao 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 RecursiveIteratorgiao diện, điều này vẫn chưa đủ để thực hiện việc duyệt foreachqua toàn bộ cây thư mục. Đây là nơi RecursiveIteratorIteratorbắ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 RecursiveIteratorIteratorthay 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ó RecursiveIteratorIteratorthể 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óagiá 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 RecursiveDirectoryIteratorcầu xóa các mục chấm ( ...) 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 RecursiveTreeIteratorCô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 foreachlầ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, RecursiveDirectoryIteratornó 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 RecursiveTreeIteratorthay vì RecursiveDirectoryIterator. Nó phải cung cấp tên cơ sở của hiện tại SplFileInfothay 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 $unicodeTreePrefixmột phần của ý chính trong Phụ lục: Do It Yourself: Make the RecursiveTreeIteratorWork Line by Line. .


Nó không trả lời các câu hỏi được đặt ra, chứa các lỗi thực tế và bỏ lỡ các điểm cốt lõi khi bạn chuyển sang tùy chỉnh lặp lại. Nhìn chung, có vẻ như một nỗ lực kém cỏi để có được tiền thưởng về một chủ đề mà bạn không biết nhiều về nó hoặc nếu có, bạn không thể chắt lọc thành câu trả lời cho các câu hỏi được đặt ra.
chào

2
Điều đó không trả lời được câu hỏi "tại sao" của tôi, bạn chỉ cần nói thêm mà không cần nói nhiều. Có thể bạn bắt đầu với một lỗi thực sự được tính? Chỉ vào nó, đừng giữ bí mật của bạn.
hakre

Câu đầu tiên sai: "RecursiveIteratorIterator là một IteratorIterator hỗ trợ…", điều này không đúng.
chào

1
@salathe: Bình luận cho bạn về phản hồi của bạn. Tôi đã chỉnh sửa câu trả lời để nhấn mạnh nó. Câu đầu tiên thực sự là sai và không đầy đủ. Tôi vẫn bỏ qua các chi tiết triển khai cụ thể RecursiveIteratorIteratorbở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 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.
hakre

1
Phần đầu tiên được cải thiện phần nào, nhưng khi bạn bắt đầu chuyển sang các ví dụ, nó vẫn chuyển sang phần không chính xác thực tế. Nếu bạn cắt câu trả lời theo quy tắc ngang, nó sẽ được cải thiện nhiều.
chào

31

Sự khác biệt của IteratorIteratorvà là RecursiveIteratorIteratorgì?

Để 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".

Trình vòng lặp đệ quy và không đệ quy

PHP có các trình vòng lặp không phải "đệ quy", chẳng hạn như ArrayIteratorFilesystemIterator. Ngoài ra còn có các trình vòng lặp "đệ quy" như RecursiveArrayIteratorRecursiveDirectoryIterator. 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.

RecursiveIteratorIterator

Đâ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 RecursiveIteratorIteratorvà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 RecursiveIteratorIteratorkhô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.

IteratorIterator

Công việc của IteratorIteratorlà lấy bất kỳ Traversableđối tượng nào và bọc nó sao cho nó thỏa mãn Iteratorgiao 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ế, DatePeriodlớp học Traversablenhư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 DatePeriodvà 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ã CallbackFilterIteratormong đợi một phiên bản của một lớp triển khai Iteratorgiao diện, mà DatePeriodkhông. Tuy nhiên, vì nó là Traversablechú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 IteratorIteratorRecursiveIteratorIterator.

Tóm lược

RecursiveIteraratorIteratorlà để lặp qua một RecursiveIterator(trình lặp "đệ quy"), khai thác hành vi đệ quy có sẵn.

IteratorIteratorlà để áp dụng Iteratorhành vi cho các Traversableđối tượng , không phải trình lặp .


Không IteratorIteratorchỉ là loại tiêu chuẩn của trình tự tuyến tính duyệt cho Traversablecác đối tượng? Những người có thể được sử dụng mà không có nó chỉ foreachnhư vậy? Và thậm chí xa hơn, không phải RecursiveIterator luôn luôn a Traversablevà do đó không chỉ IteratorIteratormà còn RecursiveIteratorIteratorluôn luôn là "để áp dụng Iteratorhà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)
hakre

Như câu trả lời của tôi đã nêu, IteratorIteratorlà một lớp là tất cả về việc gói Traversablecá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.
chào

Câu trả lời có vẻ đầy đủ thông tin. Một câu hỏi, không phải RecursiveIteratorIterator cũng bao bọc các đối tượng để chúng cũng có quyền truy cập vào hành vi của Iterator? Sự khác biệt duy nhất giữa hai điều này là RecursiveIteratorIterator có thể đi sâu vào, trong khi IteratorIterator không thể?
Mike Purcell

@salathe, bạn có biết tại sao trình lặp đệ quy (RecursiveDirectoryIterator) không thực thi hành vi hasChildren (), getChildren () không?
anru

7
+1 vì nói "có thể đệ quy". Cái tên khiến tôi lạc lối trong một thời gian dài vì bên Recursivetrong RecursiveIteratorngụ ý 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.

0

Khi được sử dụng với iterator_to_array(), RecursiveIteratorIteratorsẽ đệ 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));

Điều này là hoàn toàn sai lầm. new IteratorIterator(new ArrayIterator($array))tương đương với new ArrayIterator($array), nghĩa là, bên ngoài IteratorIteratorkhô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ó.
Câu hỏi Quolonel

0

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.