gpt4 book ai didi

php - RecursiveIteratorIterator 在 PHP 中是如何工作的?

转载 作者:行者123 更新时间:2023-12-01 17:50:38 26 4
gpt4 key购买 nike

怎么样RecursiveIteratorIterator工作?

PHP 手册没有太多记录或解释。 IteratorIterator有什么区别和 RecursiveIteratorIterator ?

最佳答案

RecursiveIteratorIterator 是混凝土 Iterator 实现 tree traversal .它使程序员能够遍历实现 RecursiveIterator 的容器对象。接口(interface),见Iterator in Wikipedia迭代器的一般原理、类型、语义和模式。

IteratorIterator 不同这是一个混凝土 Iterator以线性顺序实现对象遍历(默认情况下在其构造函数中接受任何类型的 Traversable ),RecursiveIteratorIterator允许遍历有序对象树中的所有节点,其构造函数采用 RecursiveIterator .

简而言之:RecursiveIteratorIterator允许你遍历一棵树,IteratorIterator允许您遍历列表。我很快就会用下面的一些代码示例来说明这一点。

从技术上讲,这是通过遍历节点的所有子节点(如果有)来打破线性的。这是可能的,因为根据定义,节点的所有子节点都是 RecursiveIterator .顶级Iterator然后在内部堆叠不同的 RecursiveIterator s 按它们的深度并保持一个指向当前事件子节点的指针 Iterator为遍历。

这允许访问树的所有节点。

基本原理与 IteratorIterator 相同: 一个接口(interface)指定了迭代的类型,基迭代器类是这些语义的实现。与下面的示例进行比较,使用 foreach 进行线性循环您通常不会过多考虑实现细节,除非您需要定义一个新的 Iterator (例如,当某些具体类型本身没有实现 Traversable 时)。

对于递归遍历 - 除非您不使用预定义的 Traversal已经具有递归遍历迭代 - 您通常需要实例化现有 RecursiveIteratorIterator迭代,甚至编写一个递归遍历迭代,它是 Traversable您自己可以使用 foreach 进行这种类型的遍历迭代.

Tip: You probably didn't implement the one nor the other your own, so this might be something worth to do for your practical experience of the differences they have. You find a DIY suggestion at the end of the answer.



技术差异简而言之:
  • 虽然 IteratorIterator随便拿Traversable用于线性遍历,RecursiveIteratorIterator需要更具体的RecursiveIterator在树上循环。
  • 哪里IteratorIterator暴露其主要Iterator通过 getInnerIerator() , RecursiveIteratorIterator提供当前事件子Iterator只能通过那个方法。
  • 虽然 IteratorIterator完全不知道 parent 或 child 之类的事情,RecursiveIteratorIterator也知道如何获取和遍历 child 。
  • IteratorIterator不需要一堆迭代器,RecursiveIteratorIterator有这样一个堆栈并且知道事件的子迭代器。
  • 哪里IteratorIterator由于线性和别无选择,有其顺序,RecursiveIteratorIterator可以选择进一步遍历,并且需要根据每个节点来决定(通过 mode per RecursiveIteratorIterator 决定)。
  • RecursiveIteratorIterator拥有比 IteratorIterator 更多的方法.

  • 总结一下: RecursiveIterator是一种具体类型的迭代(循环树),它适用于自己的迭代器,即 RecursiveIterator .这与 IteratorIerator 的基本原理相同,但迭代的类型不同(线性顺序)。

    理想情况下,您也可以创建自己的集合。唯一需要的是你的迭代器实现 Traversable这可以通过 Iterator 实现或 IteratorAggregate .然后你可以用 foreach .例如,某种三叉树遍历递归迭代对象以及容器对象的相应迭代接口(interface)。

    让我们回顾一些不那么抽象的真实例子。在接口(interface)、具体迭代器、容器对象和迭代语义之间,这可能不是一个坏主意。

    以目录列表为例。考虑您在磁盘上有以下文件和目录树:

    Directory 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

    如您所见,这还没有使用 IteratorIteratorRecursiveIteratorIterator .相反,它只是使用 foreachTraversable 上运行界面。

    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 .而那个只能迭代具有 RecursiveIterator interface 的容器对象。 .

    接口(interface)是一个契约。任何实现它的类都可以与 RecursiveIteratorIterator 一起使用.这种类的一个例子是 RecursiveDirectoryIterator ,这类似于 DirectoryIterator 的递归变体.

    在使用 I 字编写任何其他句子之前,让我们先看第一个代码示例:
    $dir  = new RecursiveDirectoryIterator($path);

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

    第三个示例与第一个示例几乎相同,但是它创建了一些不同的输出:
    [tree]
    ├ tree\.
    ├ tree\..
    ├ tree\dirA
    ├ tree\fileA

    好的,没有什么不同,文件名现在包含前面的路径名,但其余部分看起来也相似。

    如示例所示,即使目录对象也已经实现了 RecursiveIterator接口(interface),这还不够做 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

    当然这不会赢得选美比赛,但它表明使用递归迭代器有更多的可用信息,而不仅仅是键和值的线性顺序。偶 foreach只能表达这种线性,访问迭代器本身可以获得更多信息。

    与元信息类似,也有不同的方法可以遍历树并因此对输出进行排序。这是 Mode of the 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

    当您将其与标准遍历进行比较时,所有这些都不可用。因此,当您需要将头环绕它时,递归迭代会稍微复杂一点,但是它很容易使用,因为它的行为就像一个迭代器,您将它放入 foreach并完成。

    我认为这些是一个答案的足够例子。您可以在以下要点中找到完整的源代码以及显示漂亮的 ascii 树的示例: https://gist.github.com/3599532

    Do It Yourself: Make the RecursiveTreeIterator Work Line by Line.



    示例 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一行一行地工作..

    关于php - RecursiveIteratorIterator 在 PHP 中是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12077177/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com