猿问

RecursiveIteratorIterator如何在PHP中工作?

RecursiveIteratorIterator如何在PHP中工作?

RecursiveIteratorIterator工作怎么样?

PHP手册没有任何记录或解释。IteratorIterator和之间有什么区别RecursiveIteratorIterator


慕后森
浏览 1628回答 3
3回答

临摹微笑

RecursiveIteratorIterator是一个具体的Iterator实现树遍历。它使程序员能够遍历实现RecursiveIterator接口的容器对象,请参阅Wikipedia中的Iterator,了解迭代器的一般原理,类型,语义和模式。不同之处IteratorIterator在于具体的Iterator实现对象以线性顺序遍历(并且默认接受Traversable其构造函数中的任何类型),RecursiveIteratorIterator允许循环遍历对象的有序树中的所有节点,并且其构造函数采用a RecursiveIterator。简而言之:RecursiveIteratorIterator允许您循环遍历树,IteratorIterator允许您循环遍历列表。我将在下面展示一些代码示例。从技术上讲,这可以通过遍历所有节点的子节点(如果有的话)来消除线性。这是可能的,因为根据定义,节点的所有子节点都是a RecursiveIterator。然后,toplevel Iterator在内部RecursiveIterator通过它们的深度堆叠不同的s,并保持指向当前活动子的指针以Iterator进行遍历。这允许访问树的所有节点。基本原理与以下内容相同IteratorIterator:接口指定迭代的类型,基本迭代器类是这些语义的实现。与下面的示例相比,对于线性循环,foreach除非需要定义新的Iterator(例如,当某些具体类型本身未实现时Traversable),否则通常不会考虑实现细节。对于递归遍历 - 除非您不使用Traversal已经具有递归遍历迭代的预定义- 您通常需要实例化现有RecursiveIteratorIterator迭代,或者甚至编写一个递归遍历迭代,这是Traversable您自己进行这种类型的遍历迭代foreach。提示:您可能没有实现自己的那个,所以这可能是您在实际经验中所做的差异。你会在答案的最后找到一个DIY建议。技术差异简称:虽然IteratorIterator需要任何Traversable线性遍历,但RecursiveIteratorIterator需要更具体RecursiveIterator的循环遍历树。其中IteratorIterator公开其主要Iterator通过getInnerIerator(),RecursiveIteratorIterator提供当前活跃子Iterator只通过该方法。虽然IteratorIterator完全不了解父母或孩子之类的东西,但RecursiveIteratorIterator也知道如何获得和穿越孩子。IteratorIterator不需要堆栈的迭代器,RecursiveIteratorIterator有这样的堆栈并且知道活动的子迭代器。IteratorIterator由于线性而没有选择,其订单在哪里,RecursiveIteratorIterator可以选择进一步遍历并需要根据每个节点决定(通过模式RecursiveIteratorIterator确定)。RecursiveIteratorIterator有更多的方法比IteratorIterator。总结一下:RecursiveIterator是一种在其自己的迭代器上工作的具体迭代类型(在树上循环),即RecursiveIterator。这与基本原理相同IteratorIerator,但迭代类型不同(线性顺序)。理想情况下,您也可以创建自己的套装。唯一需要的是你的迭代器Traversable通过Iterator或实现可能的IteratorAggregate。然后你可以使用它foreach。例如,某种三元树遍历递归迭代对象以及容器对象的相应迭代接口。让我们回顾一些不那么抽象的现实例子。在接口,具体迭代器,容器对象和迭代语义之间,这可能不是一个坏主意。以目录列表为例。考虑您在磁盘上有以下文件和目录树:虽然具有线性顺序的迭代器只遍历顶层文件夹和文件(单个目录列表),但递归迭代器也会遍历子文件夹并列出所有文件夹和文件(包含其子目录列表的目录列表):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第二个例子中的a IteratorIterator。这显示了迭代器具有的灵活性:您可以相互替换它们,内部的代码foreach只是继续按预期工作。让我们开始获取整个列表,包括子目录。正如我们现在已经指定了迭代的类型,让我们考虑将其更改为另一种迭代类型。我们知道我们现在需要遍历整个树,而不仅仅是第一层。要使用简单的工作,foreach我们需要一种不同类型的迭代器:RecursiveIteratorIterator。而且只能迭代具有RecursiveIterator接口的容器对象。界面是合同。实现它的任何类都可以与RecursiveIteratorIterator。一起使用。这样一个类的一个例子是RecursiveDirectoryIterator,它类似于递归变体DirectoryIterator。让我们在用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当然这不会赢得选美比赛,但它表明,使用递归迭代器可以获得更多信息,而不仅仅是键和值的线性顺序。即使foreach只能表达这种线性,访问迭代器本身也可以获得更多信息。与元信息类似,如何遍历树并因此对输出进行排序也有不同的方法。这是模式,RecursiveIteratorIterator可以使用构造函数进行设置。下一个示例将告诉RecursiveDirectoryIterator删除点条目(.和..),因为我们不需要它们。但是,递归模式也将更改为SELF_FIRST在子项(子目录中的文件和子子目录)之前将父元素(子目录)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因此,对于目录示例,递归模式控制返回树中的brach或leaf的内容和时间: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-trees的示例: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与a结合使用时,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工作。。

千巷猫影

是什么的差异IteratorIterator和RecursiveIteratorIterator?要理解这两个迭代器之间的区别,首先必须先了解一下使用的命名约定以及“递归”迭代器的含义。递归和非递归迭代器PHP具有非“递归”迭代器,例如ArrayIterator和FilesystemIterator。还有“递归”迭代器,如RecursiveArrayIterator和RecursiveDirectoryIterator。后者有方法可以将它们钻进去,前者则没有。当这些迭代器的实例自行循环时,即使是递归的,即使循环遍历嵌套数组或带有子目录的目录,这些值也只来自“顶层”。递归迭代器实现递归行为(via hasChildren(),getChildren())但不利用它。将递归迭代器视为“递归”迭代器可能更好,它们具有递归迭代的能力,但简单地迭代其中一个类的实例将不会这样做。要利用递归行为,请继续阅读。RecursiveIteratorIterator这是RecursiveIteratorIterator进入游戏的地方。它具有如何调用“递归”迭代器的知识,以便在正常的平坦循环中向下钻取结构。它将递归行为付诸行动。它主要完成跨越迭代器中每个值的工作,查看是否有“子”进入或不进入,以及进入和退出这些子集合。你将一个实例粘贴RecursiveIteratorIterator到一个foreach中,它潜入结构中,这样你就不必这样做了。如果RecursiveIteratorIterator没有使用,你必须编写自己的递归循环来利用递归行为,检查“递归”迭代器hasChildren()和使用getChildren()。这是一个简短的概述RecursiveIteratorIterator,它与它有什么不同IteratorIterator?好吧,你基本上都在问同样的问题:小猫和树之间有什么区别?只是因为两者都出现在同一个百科全书(或手册,对于迭代器)并不意味着你应该在两者之间混淆。IteratorIterator它的工作IteratorIterator是获取任何Traversable对象,并将其包装以使其满足Iterator接口。这样做的用途是能够在非迭代器对象上应用特定于迭代器的行为。举一个实际的例子,这个DatePeriod课程Traversable不是一个Iterator。因此,我们可以循环其值,foreach()但不能执行我们通常使用迭代器的其他事情,例如过滤。任务:在接下来的四周的周一,周三和周五进行循环。是的,这是由琐碎foreach-ing比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(…));正如您所看到的,这与迭代迭代器类和递归没有任何关系,其中存在IteratorIterator和之间的区别RecursiveIteratorIterator。摘要RecursiveIteraratorIterator用于迭代RecursiveIterator(“递归”迭代器),利用可用的递归行为。IteratorIterator用于将Iterator行为应用于非迭代器,Traversable对象。

森栏

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
随时随地看视频慕课网APP
我要回答