Se você perguntar como RecursiveIteratorIteratorfunciona, já entendeu como IteratorIteratorfunciona? Quer dizer, é basicamente o mesmo, apenas a interface que é consumida pelos dois é diferente. E você está mais interessado em alguns exemplos ou deseja ver a diferença da implementação do código C subjacente?
hakre,
@Gordon Eu não tinha certeza de como o loop foreach único pode atravessar todos os elementos na estrutura de árvore
varuog
@hakra, agora estou tentando estudar todas as interfaces incorporadas, bem como a interface spl e a implementação do iterador. Estava interessado em saber como funciona em segundo plano com o loop forach com alguns exemplos.
Varuog
@hakre Ambos são muito diferentes. IteratorIteratormapas Iteratore IteratorAggregateem um Iterator, onde REcusiveIteratorIteratoré usado para atravessar recusivly umRecursiveIterator
Diferentemente de IteratorIteratorqual é um Iteratorobjeto de implementação concreto atravessando em ordem linear (e por padrão aceitando qualquer tipo de Traversableem seu construtor), o RecursiveIteratorIteratorpermite fazer um loop sobre todos os nós em uma árvore ordenada de objetos e seu construtor leva a RecursiveIterator.
Resumindo: RecursiveIteratorIteratorpermite que você faça um loop em uma árvore, IteratorIteratorpermite que você faça um loop em uma lista. Eu mostro isso com alguns exemplos de código abaixo em breve.
Tecnicamente, isso funciona quebrando a linearidade ao percorrer todos os filhos de um nó (se houver). Isso é possível porque, por definição, todos os filhos de um nó são novamente a RecursiveIterator. O nível superior Iteratorentão empilha internamente os diferentes programas RecursiveIteratorpor sua profundidade e mantém um ponteiro para o sub ativo atual Iteratorpara travessia.
Isso permite visitar todos os nós de uma árvore.
Os princípios básicos são os mesmos de IteratorIterator: Uma interface especifica o tipo de iteração e a classe base do iterador é a implementação dessa semântica. Compare com os exemplos abaixo, para loop linear com foreachvocê normalmente não pense muito sobre os detalhes de implementação, a menos que você precise definir um novo Iterator(por exemplo, quando algum tipo concreto não implementa Traversable).
Para traversal recursivo - a menos que você não use um pré-definido Traversalque já tenha iteração traversal recursiva - você normalmente precisa instanciar a RecursiveIteratorIteratoriteração existente ou até mesmo escrever uma iteração traversal recursiva que seja Traversablesua para ter este tipo de iteração traversal foreach.
Dica: Você provavelmente não implementou nem um nem outro por conta própria, então isso pode valer a pena fazer por sua experiência prática das diferenças que eles têm. Você encontra uma sugestão de DIY no final da resposta.
Em resumo, diferenças técnicas:
Enquanto IteratorIteratorleva any Traversablepara travessia linear, RecursiveIteratorIteratorprecisa de um RecursiveIteratorloop mais específico para uma árvore.
Onde IteratorIteratorexpõe sua Iteratorvia principal getInnerIerator(), RecursiveIteratorIteratorfornece a sub-rotina ativa atual Iteratorapenas por meio desse método.
Embora não IteratorIteratoresteja totalmente ciente de nada como pai ou filhos, RecursiveIteratorIteratorsabe como obter e atravessar os filhos também.
IteratorIteratornão precisa de uma pilha de iteradores, RecursiveIteratorIteratortem essa pilha e conhece o sub-iterador ativo.
Onde IteratorIteratortem sua ordem devido à linearidade e nenhuma escolha, RecursiveIteratorIteratortem uma escolha para travessia adicional e precisa decidir por cada nó (decidido por modo porRecursiveIteratorIterator ).
RecursiveIteratorIteratortem mais métodos do que IteratorIterator.
Para resumir: RecursiveIteratoré um tipo concreto de iteração (looping sobre uma árvore) que funciona em seus próprios iteradores, a saber RecursiveIterator. Esse é o mesmo princípio subjacente de IteratorIerator, mas o tipo de iteração é diferente (ordem linear).
Idealmente, você também pode criar seu próprio conjunto. A única coisa necessária é que seu iterador implemente o Traversableque é possível por meio de Iteratorou IteratorAggregate. Então você pode usá-lo com foreach. Por exemplo, algum tipo de objeto de iteração recursiva de travessia de árvore ternária junto com a interface de iteração correspondente para o (s) objeto (s) recipiente.
Vamos revisar alguns exemplos da vida real que não são tão abstratos. Entre interfaces, iteradores concretos, objetos de contêiner e semântica de iteração, talvez não seja uma ideia tão ruim.
Pegue uma lista de diretórios como exemplo. Considere que você tem o seguinte arquivo e árvore de diretório no disco:
Enquanto um iterador com ordem linear apenas atravessa a pasta e os arquivos de nível superior (uma única listagem de diretório), o iterador recursivo também percorre as subpastas e lista todas as pastas e arquivos (uma listagem de diretório com listagens de seus subdiretórios):
Non-RecursiveRecursive======================[tree][tree]├ dirA ├ dirA
└ fileA │├ dirB
││└ fileD
│├ fileB
│└ fileC
└ fileA
Você pode facilmente comparar isso com o IteratorIteratorqual não faz recursão para percorrer a árvore de diretórios. E o RecursiveIteratorIteratorque pode penetrar na árvore, conforme mostra a lista Recursiva.
O exemplo de saída para a estrutura de diretório acima é:
[tree]├.├..├ dirA
├ fileA
Como você pode ver, isso ainda não está usando IteratorIteratorou RecursiveIteratorIterator. Em vez disso, basta apenas usar o foreachque opera na Traversableinterface.
Como, foreachpor padrão, só conhece o tipo de iteração chamada ordem linear, podemos querer especificar o tipo de iteração explicitamente. À primeira vista pode parecer muito prolixo, mas para fins de demonstração (e para fazer a diferença com RecursiveIteratorIteratormais visível posteriormente), vamos especificar o tipo linear de iteração especificando explicitamente o IteratorIteratortipo de iteração para a listagem de diretório:
$files =newIteratorIterator($dir);
echo "[$path]\n";foreach($files as $file){
echo " ├ $file\n";}
Este exemplo é quase idêntico ao primeiro, a diferença é que $filesagora é um IteratorIteratortipo de iteração para Traversable$dir:
$files =newIteratorIterator($dir);
Como de costume, o ato de iteração é realizado por foreach:
foreach($files as $file){
A saída é exatamente a mesma. Então, o que é diferente? Diferente é o objeto usado no foreach. No primeiro exemplo é um, DirectoryIteratorno segundo exemplo é o IteratorIterator. Isso mostra a flexibilidade que os iteradores têm: você pode substituí-los uns pelos outros, o código interno foreachapenas continua a funcionar conforme o esperado.
Vamos começar a obter a lista completa, incluindo subdiretórios.
Como agora especificamos o tipo de iteração, vamos considerar alterá-lo para outro tipo de iteração.
Sabemos que precisamos atravessar a árvore inteira agora, não apenas o primeiro nível. Para ter esse trabalho com um simples foreachprecisamos de um tipo diferente de iterator: RecursiveIteratorIterator. E isso só pode ser iterado em objetos de contêiner que possuem a RecursiveIteratorinterface .
A interface é um contrato. Qualquer classe que o implemente pode ser usada junto com o RecursiveIteratorIterator. Um exemplo dessa classe é o RecursiveDirectoryIterator, que é algo como a variante recursiva de DirectoryIterator.
Vamos ver um primeiro exemplo de código antes de escrever qualquer outra frase com a palavra I:
$dir =newRecursiveDirectoryIterator($path);
echo "[$path]\n";foreach($dir as $file){
echo " ├ $file\n";}
Este terceiro exemplo é quase idêntico ao primeiro, no entanto, cria algumas saídas diferentes:
[tree]├ tree\.
├ tree\..├ tree\dirA
├ tree\fileA
Ok, não tão diferente, o nome do arquivo agora contém o nome do caminho na frente, mas o resto também parece semelhante.
Como mostra o exemplo, mesmo o objeto diretório já implementa a RecursiveIteratorinterface, isso ainda não é suficiente para fazer foreachpercorrer toda a árvore de diretório. É aqui que o RecursiveIteratorIteratorentra em ação. O Exemplo 4 mostra como:
$files =newRecursiveIteratorIterator($dir);
echo "[$path]\n";foreach($files as $file){
echo " ├ $file\n";}
Usar o em RecursiveIteratorIteratorvez de apenas o $dirobjeto anterior fará foreachcom que percorra todos os arquivos e diretórios de maneira recursiva. Em seguida, isso lista todos os arquivos, pois o tipo de iteração do objeto foi especificado agora:
Isso já deve demonstrar a diferença entre a travessia plana e em árvore. O RecursiveIteratorIteratoré capaz de percorrer qualquer estrutura semelhante a uma árvore como uma lista de elementos. Como há mais informações (como o nível em que a iteração ocorre atualmente), é possível acessar o objeto iterador ao iterar sobre ele e, por exemplo, indentar a saída:
Claro, isso não ganha um concurso de beleza, mas mostra que, com o iterador recursivo, há mais informações disponíveis do que apenas a ordem linear de chave e valor . A Even foreachsó pode expressar esse tipo de linearidade, acessando o próprio iterador permite obter mais informações.
Semelhante à meta-informação, também existem diferentes maneiras possíveis de percorrer a árvore e, portanto, ordenar a saída. Este é o modo deRecursiveIteratorIterator e pode ser definido com o construtor.
O próximo exemplo dirá ao RecursiveDirectoryIteratorpara remover as entradas de pontos ( .e ..), pois não precisamos delas. Mas também o modo de recursão será alterado para levar o elemento pai (o subdiretório) primeiro ( SELF_FIRST) antes dos filhos (os arquivos e sub-subdiretórios no subdiretório):
Quando você compara isso com o percurso padrão, todas essas coisas não estão disponíveis. A iteração recursiva, portanto, é um pouco mais complexa quando você precisa envolvê-la em sua cabeça, no entanto, é fácil de usar porque se comporta como um iterador, você a coloca em um foreache pronto.
Acho que esses são exemplos suficientes para uma resposta. Você pode encontrar o código-fonte completo, bem como um exemplo para exibir árvores ascii de boa aparência nesta essência: https://gist.github.com/3599532
Faça você mesmo: faça o RecursiveTreeIteratortrabalho linha por linha.
O Exemplo 5 demonstrou que há meta-informações disponíveis sobre o estado do iterador. No entanto, esta foi propositadamente demonstrado dentro da foreachiteração. Na vida real, isso pertence naturalmente ao RecursiveIterator.
Um exemplo melhor é o RecursiveTreeIterator, ele cuida de indentação, prefixação e assim por diante. Veja o seguinte fragmento de código:
Quando usado em combinação com a RecursiveDirectoryIterator, exibe o nome do caminho completo e não apenas o nome do arquivo. O resto parece bom. Isso ocorre porque os nomes dos arquivos são gerados por SplFileInfo. Esses devem ser exibidos como o nome de base. O resultado desejado é o seguinte:
Crie uma classe de decorador que pode ser usada com em RecursiveTreeIteratorvez de RecursiveDirectoryIterator. Ele deve fornecer o nome de base do atual em SplFileInfovez do nome do caminho. O fragmento de código final poderia ser assim:
Esses fragmentos, inclusive, $unicodeTreePrefixsão parte da essência do Apêndice: Faça você mesmo: faça o RecursiveTreeIteratortrabalho linha por linha. .
Ele não responde às perguntas feitas, contém erros factuais e perde pontos principais quando você passa a personalizar a iteração. Em suma, parece uma tentativa pobre de obter recompensa por um tópico sobre o qual você não sabe muito ou, se sabe, não pode destilar em uma resposta às perguntas feitas.
salathe
2
Bem, o que não responde à minha pergunta "por que", você apenas faz mais palavras sem dizer muito. Talvez você comece realmente com um erro que conta? Aponte para isso, não mantenha isso em segredo.
hakre,
A primeira frase está incorreta: "RecursiveIteratorIterator é um IteratorIterator que suporta ...", isso não é verdade.
Salathe 02 de
1
@salathe: Obrigado por seu feedback. Eu editei a resposta para abordá-la. A primeira frase de fato estava errada e incompleta. Ainda deixei de fora os detalhes de implementação concretos RecursiveIteratorIteratorporque isso é comum com outros tipos, mas dei algumas informações técnicas de como ele realmente funciona. Acho que os exemplos mostram bem as diferenças: o tipo de iteração é a principal diferença entre os dois. Não tenho ideia se você comprar o tipo de iteração que você inventa de uma forma um pouco diferente, mas IMHO não é fácil com os tipos de semântica de iteração.
hakre,
1
A primeira parte foi melhorada um pouco, mas uma vez que você começa a passar para os exemplos, ela ainda apresenta imprecisões factuais. Se você cortar a resposta na régua horizontal, será muito melhorado.
salathe
31
Qual é a diferença de IteratorIteratore RecursiveIteratorIterator?
Para entender a diferença entre esses dois iteradores, deve-se primeiro entender um pouco sobre as convenções de nomenclatura usadas e o que queremos dizer com iteradores "recursivos".
Iteradores recursivos e não recursivos
O PHP possui iteradores não "recursivos", como ArrayIteratore FilesystemIterator. Existem também iteradores "recursivos", como o RecursiveArrayIteratore RecursiveDirectoryIterator. Os últimos têm métodos que os permitem aprofundar, os primeiros não.
Quando as instâncias desses iteradores são executadas em loop por conta própria, mesmo os recursivos, os valores só vêm do nível "superior", mesmo que estejam em loop em uma matriz ou diretório aninhado com subdiretórios.
Os iteradores recursivos implementam comportamento recursivo (via hasChildren(), getChildren()) , mas não o exploram .
Pode ser melhor pensar nos iteradores recursivos como iteradores "recursíveis", eles têm a capacidade de serem iterados recursivamente, mas simplesmente iterar sobre uma instância de uma dessas classes não fará isso. Para explorar o comportamento recursivo, continue lendo.
RecursiveIteratorIterator
É aqui que RecursiveIteratorIteratorentra o jogo. Ele tem o conhecimento de como chamar os iteradores "recursíveis" de forma a detalhar a estrutura em um loop normal e plano. Ele coloca o comportamento recursivo em ação. Essencialmente, ele faz o trabalho de passar por cima de cada um dos valores no iterador, procurando ver se há "filhos" para recursar ou não, e entrar e sair dessas coleções de filhos. Você coloca uma instância de RecursiveIteratorIteratorem um foreach, e ele mergulha na estrutura para que você não precise fazer isso.
Se o RecursiveIteratorIteratornão foi usado, você teria que escrever seus próprios loops recursivos para explorar o comportamento recursivo, comparando com o iterador "recursível" hasChildren()e usando getChildren().
Essa é uma breve visão geral de RecursiveIteratorIteratorcomo é diferente IteratorIterator? Bem, você está basicamente fazendo o mesmo tipo de pergunta que Qual é a diferença entre um gatinho e uma árvore? Só porque ambos aparecem na mesma enciclopédia (ou manual, para os iteradores) não significa que você deva se confundir entre os dois.
IteratorIterator
O trabalho do IteratorIteratoré pegar qualquer Traversableobjeto e envolvê-lo de forma que satisfaça a Iteratorinterface. Um uso para isso é ser capaz de aplicar o comportamento específico do iterador no objeto não iterador.
Para dar um exemplo prático, a DatePeriodclasse é, Traversablemas não um Iterator. Como tal, podemos fazer um loop sobre seus valores com, foreach()mas não podemos fazer outras coisas que normalmente faríamos com um iterador, como a filtragem.
TAREFA : Loop ao longo das segundas, quartas e sextas-feiras das próximas quatro semanas.
Sim, isso é trivial por foreach-ing no DatePeriode usando um if()dentro do loop; mas esse não é o ponto deste exemplo!
$period =newDatePeriod(newDateTime,newDateInterval('P1D'),28);
$dates =newCallbackFilterIterator($period,function($date){return in_array($date->format('l'), array('Monday','Wednesday','Friday'));});foreach($dates as $date){…}
O trecho acima não funcionará porque o CallbackFilterIteratorespera uma instância de uma classe que implementa a Iteratorinterface, que DatePeriodnão. No entanto, como é Traversable, podemos facilmente satisfazer esse requisito usando IteratorIterator.
$period =newIteratorIterator(newDatePeriod(…));
Como você pode ver, isso não tem nada a ver com a iteração em classes de iteradores nem com a recursão, e é aí que reside a diferença entre IteratorIteratore RecursiveIteratorIterator.
Resumo
RecursiveIteraratorIteratoré para iterar sobre um RecursiveIterator(iterador "recursível"), explorando o comportamento recursivo que está disponível.
IteratorIteratoré para aplicar o Iteratorcomportamento a Traversableobjetos não iterativos .
Não é IteratorIteratorapenas o tipo padrão de passagem de ordem linear para Traversableobjetos? Aqueles que poderiam ser usados sem ele exatamente foreachcomo estão? E, ainda mais, não é RecursiveIteratorsempre um Traversablee, portanto, não apenas, IteratorIteratormas também RecursiveIteratorIteratorsempre "para aplicar Iteratorcomportamento a objetos não iteradores, Traversable" ? (Agora eu diria que foreachaplica o tipo de iteração por meio do objeto iterador em objetos contêiner que implementam uma interface do tipo iterador, portanto, esses são objetos contêiner-iterador, sempre Traversable)
hakre
Como minha resposta afirma, IteratorIteratoré uma classe que trata de envolver Traversableobjetos em um Iterator. Mais nada . Você parece estar aplicando o termo de forma mais geral.
salathe
Resposta aparentemente informativa. Uma pergunta: RecursiveIteratorIterator também não agruparia objetos para que eles também tivessem acesso ao comportamento de Iterator? A única diferença entre os dois seria que o RecursiveIteratorIterator pode fazer drill down, enquanto o IteratorIterator não pode?
Mike Purcell
@salathe, você sabe por que o iterador recursivo (RecursiveDirectoryIterator) não implementa o comportamento hasChildren (), getChildren ()?
anru,
7
1 para dizer "recursível". O nome me desviou por muito tempo porque o Recursivein RecursiveIteratorimplica comportamento, enquanto um nome mais adequado teria sido aquele que descreve capacidade, como RecursibleIterator.
cabra
0
Quando usado com iterator_to_array(), RecursiveIteratorIteratorpercorrerá recursivamente o array para encontrar todos os valores. O que significa que ele irá nivelar a matriz original.
IteratorIterator manterá a estrutura hierárquica original.
Isso é totalmente enganoso. new IteratorIterator(new ArrayIterator($array))equivale a new ArrayIterator($array), isto é, o externo não IteratorIteratorestá fazendo nada. Além disso, o achatamento da saída não tem nada a ver com iterator_to_array- ele simplesmente converte o iterador em uma matriz. O achatamento é uma propriedade da maneira como RecursiveArrayIteratorpercorre seu iterador interno.
Quolonel Perguntas
0
RecursiveDirectoryIterator ele exibe o nome do caminho completo e não apenas o nome do arquivo. O resto parece bom. Isso ocorre porque os nomes dos arquivos são gerados por SplFileInfo. Em vez disso, eles devem ser exibidos como o nome de base. O resultado desejado é o seguinte:
RecursiveIteratorIterator
funciona, já entendeu comoIteratorIterator
funciona? Quer dizer, é basicamente o mesmo, apenas a interface que é consumida pelos dois é diferente. E você está mais interessado em alguns exemplos ou deseja ver a diferença da implementação do código C subjacente?IteratorIterator
mapasIterator
eIteratorAggregate
em umIterator
, ondeREcusiveIteratorIterator
é usado para atravessar recusivly umRecursiveIterator
Respostas:
RecursiveIteratorIterator
é uma travessia de árvore deIterator
implementação concreta . Ele permite que um programador atravesse um objeto contêiner que implementa a interface, consulte Iterator na Wikipedia para os princípios gerais, tipos, semânticas e padrões de iteradores.RecursiveIterator
Diferentemente de
IteratorIterator
qual é umIterator
objeto de implementação concreto atravessando em ordem linear (e por padrão aceitando qualquer tipo deTraversable
em seu construtor), oRecursiveIteratorIterator
permite fazer um loop sobre todos os nós em uma árvore ordenada de objetos e seu construtor leva aRecursiveIterator
.Resumindo:
RecursiveIteratorIterator
permite que você faça um loop em uma árvore,IteratorIterator
permite que você faça um loop em uma lista. Eu mostro isso com alguns exemplos de código abaixo em breve.Tecnicamente, isso funciona quebrando a linearidade ao percorrer todos os filhos de um nó (se houver). Isso é possível porque, por definição, todos os filhos de um nó são novamente a
RecursiveIterator
. O nível superiorIterator
então empilha internamente os diferentes programasRecursiveIterator
por sua profundidade e mantém um ponteiro para o sub ativo atualIterator
para travessia.Isso permite visitar todos os nós de uma árvore.
Os princípios básicos são os mesmos de
IteratorIterator
: Uma interface especifica o tipo de iteração e a classe base do iterador é a implementação dessa semântica. Compare com os exemplos abaixo, para loop linear comforeach
você normalmente não pense muito sobre os detalhes de implementação, a menos que você precise definir um novoIterator
(por exemplo, quando algum tipo concreto não implementaTraversable
).Para traversal recursivo - a menos que você não use um pré-definido
Traversal
que já tenha iteração traversal recursiva - você normalmente precisa instanciar aRecursiveIteratorIterator
iteração existente ou até mesmo escrever uma iteração traversal recursiva que sejaTraversable
sua para ter este tipo de iteração traversalforeach
.Em resumo, diferenças técnicas:
IteratorIterator
leva anyTraversable
para travessia linear,RecursiveIteratorIterator
precisa de umRecursiveIterator
loop mais específico para uma árvore.IteratorIterator
expõe suaIterator
via principalgetInnerIerator()
,RecursiveIteratorIterator
fornece a sub-rotina ativa atualIterator
apenas por meio desse método.IteratorIterator
esteja totalmente ciente de nada como pai ou filhos,RecursiveIteratorIterator
sabe como obter e atravessar os filhos também.IteratorIterator
não precisa de uma pilha de iteradores,RecursiveIteratorIterator
tem essa pilha e conhece o sub-iterador ativo.IteratorIterator
tem sua ordem devido à linearidade e nenhuma escolha,RecursiveIteratorIterator
tem uma escolha para travessia adicional e precisa decidir por cada nó (decidido por modo porRecursiveIteratorIterator
).RecursiveIteratorIterator
tem mais métodos do queIteratorIterator
.Para resumir:
RecursiveIterator
é um tipo concreto de iteração (looping sobre uma árvore) que funciona em seus próprios iteradores, a saberRecursiveIterator
. Esse é o mesmo princípio subjacente deIteratorIerator
, mas o tipo de iteração é diferente (ordem linear).Idealmente, você também pode criar seu próprio conjunto. A única coisa necessária é que seu iterador implemente o
Traversable
que é possível por meio deIterator
ouIteratorAggregate
. Então você pode usá-lo comforeach
. Por exemplo, algum tipo de objeto de iteração recursiva de travessia de árvore ternária junto com a interface de iteração correspondente para o (s) objeto (s) recipiente.Vamos revisar alguns exemplos da vida real que não são tão abstratos. Entre interfaces, iteradores concretos, objetos de contêiner e semântica de iteração, talvez não seja uma ideia tão ruim.
Pegue uma lista de diretórios como exemplo. Considere que você tem o seguinte arquivo e árvore de diretório no disco:
Enquanto um iterador com ordem linear apenas atravessa a pasta e os arquivos de nível superior (uma única listagem de diretório), o iterador recursivo também percorre as subpastas e lista todas as pastas e arquivos (uma listagem de diretório com listagens de seus subdiretórios):
Você pode facilmente comparar isso com o
IteratorIterator
qual não faz recursão para percorrer a árvore de diretórios. E oRecursiveIteratorIterator
que pode penetrar na árvore, conforme mostra a lista Recursiva.No primeiro um exemplo muito básico com um
DirectoryIterator
que implementaTraversable
o que permiteforeach
a iterate sobre ele:O exemplo de saída para a estrutura de diretório acima é:
Como você pode ver, isso ainda não está usando
IteratorIterator
ouRecursiveIteratorIterator
. Em vez disso, basta apenas usar oforeach
que opera naTraversable
interface.Como,
foreach
por padrão, só conhece o tipo de iteração chamada ordem linear, podemos querer especificar o tipo de iteração explicitamente. À primeira vista pode parecer muito prolixo, mas para fins de demonstração (e para fazer a diferença comRecursiveIteratorIterator
mais visível posteriormente), vamos especificar o tipo linear de iteração especificando explicitamente oIteratorIterator
tipo de iteração para a listagem de diretório:Este exemplo é quase idêntico ao primeiro, a diferença é que
$files
agora é umIteratorIterator
tipo de iteração paraTraversable
$dir
:Como de costume, o ato de iteração é realizado por
foreach
:A saída é exatamente a mesma. Então, o que é diferente? Diferente é o objeto usado no
foreach
. No primeiro exemplo é um,DirectoryIterator
no segundo exemplo é oIteratorIterator
. Isso mostra a flexibilidade que os iteradores têm: você pode substituí-los uns pelos outros, o código internoforeach
apenas continua a funcionar conforme o esperado.Vamos começar a obter a lista completa, incluindo subdiretórios.
Como agora especificamos o tipo de iteração, vamos considerar alterá-lo para outro tipo de iteração.
Sabemos que precisamos atravessar a árvore inteira agora, não apenas o primeiro nível. Para ter esse trabalho com um simples
foreach
precisamos de um tipo diferente de iterator:RecursiveIteratorIterator
. E isso só pode ser iterado em objetos de contêiner que possuem aRecursiveIterator
interface .A interface é um contrato. Qualquer classe que o implemente pode ser usada junto com o
RecursiveIteratorIterator
. Um exemplo dessa classe é oRecursiveDirectoryIterator
, que é algo como a variante recursiva deDirectoryIterator
.Vamos ver um primeiro exemplo de código antes de escrever qualquer outra frase com a palavra I:
Este terceiro exemplo é quase idêntico ao primeiro, no entanto, cria algumas saídas diferentes:
Ok, não tão diferente, o nome do arquivo agora contém o nome do caminho na frente, mas o resto também parece semelhante.
Como mostra o exemplo, mesmo o objeto diretório já implementa a
RecursiveIterator
interface, isso ainda não é suficiente para fazerforeach
percorrer toda a árvore de diretório. É aqui que oRecursiveIteratorIterator
entra em ação. O Exemplo 4 mostra como:Usar o em
RecursiveIteratorIterator
vez de apenas o$dir
objeto anterior faráforeach
com que percorra todos os arquivos e diretórios de maneira recursiva. Em seguida, isso lista todos os arquivos, pois o tipo de iteração do objeto foi especificado agora:Isso já deve demonstrar a diferença entre a travessia plana e em árvore. O
RecursiveIteratorIterator
é capaz de percorrer qualquer estrutura semelhante a uma árvore como uma lista de elementos. Como há mais informações (como o nível em que a iteração ocorre atualmente), é possível acessar o objeto iterador ao iterar sobre ele e, por exemplo, indentar a saída:E o resultado do Exemplo 5 :
Claro, isso não ganha um concurso de beleza, mas mostra que, com o iterador recursivo, há mais informações disponíveis do que apenas a ordem linear de chave e valor . A Even
foreach
só pode expressar esse tipo de linearidade, acessando o próprio iterador permite obter mais informações.Semelhante à meta-informação, também existem diferentes maneiras possíveis de percorrer a árvore e, portanto, ordenar a saída. Este é o modo de
RecursiveIteratorIterator
e pode ser definido com o construtor.O próximo exemplo dirá ao
RecursiveDirectoryIterator
para remover as entradas de pontos (.
e..
), pois não precisamos delas. Mas também o modo de recursão será alterado para levar o elemento pai (o subdiretório) primeiro (SELF_FIRST
) antes dos filhos (os arquivos e sub-subdiretórios no subdiretório):A saída agora mostra as entradas do subdiretório listadas corretamente, se você comparar com a saída anterior, aquelas não estavam lá:
O modo de recursão, portanto, controla o que e quando um brach ou folha na árvore é retornado, para o exemplo de diretório:
LEAVES_ONLY
(padrão): Listar apenas arquivos, sem diretórios.SELF_FIRST
(acima): lista o diretório e os arquivos nele contidos.CHILD_FIRST
(sem exemplo): Liste os arquivos no subdiretório primeiro e, em seguida, no diretório.Saída do Exemplo 5 com os outros dois modos:
Quando você compara isso com o percurso padrão, todas essas coisas não estão disponíveis. A iteração recursiva, portanto, é um pouco mais complexa quando você precisa envolvê-la em sua cabeça, no entanto, é fácil de usar porque se comporta como um iterador, você a coloca em um
foreach
e pronto.Acho que esses são exemplos suficientes para uma resposta. Você pode encontrar o código-fonte completo, bem como um exemplo para exibir árvores ascii de boa aparência nesta essência: https://gist.github.com/3599532
O Exemplo 5 demonstrou que há meta-informações disponíveis sobre o estado do iterador. No entanto, esta foi propositadamente demonstrado dentro da
foreach
iteração. Na vida real, isso pertence naturalmente aoRecursiveIterator
.Um exemplo melhor é o
RecursiveTreeIterator
, ele cuida de indentação, prefixação e assim por diante. Veja o seguinte fragmento de código:O
RecursiveTreeIterator
objetivo é trabalhar linha por linha, a saída é bastante direta com um pequeno problema:Quando usado em combinação com a
RecursiveDirectoryIterator
, exibe o nome do caminho completo e não apenas o nome do arquivo. O resto parece bom. Isso ocorre porque os nomes dos arquivos são gerados porSplFileInfo
. Esses devem ser exibidos como o nome de base. O resultado desejado é o seguinte:Crie uma classe de decorador que pode ser usada com em
RecursiveTreeIterator
vez deRecursiveDirectoryIterator
. Ele deve fornecer o nome de base do atual emSplFileInfo
vez do nome do caminho. O fragmento de código final poderia ser assim:Esses fragmentos, inclusive,
$unicodeTreePrefix
são parte da essência do Apêndice: Faça você mesmo: faça oRecursiveTreeIterator
trabalho linha por linha. .fonte
RecursiveIteratorIterator
porque isso é comum com outros tipos, mas dei algumas informações técnicas de como ele realmente funciona. Acho que os exemplos mostram bem as diferenças: o tipo de iteração é a principal diferença entre os dois. Não tenho ideia se você comprar o tipo de iteração que você inventa de uma forma um pouco diferente, mas IMHO não é fácil com os tipos de semântica de iteração.Para entender a diferença entre esses dois iteradores, deve-se primeiro entender um pouco sobre as convenções de nomenclatura usadas e o que queremos dizer com iteradores "recursivos".
Iteradores recursivos e não recursivos
O PHP possui iteradores não "recursivos", como
ArrayIterator
eFilesystemIterator
. Existem também iteradores "recursivos", como oRecursiveArrayIterator
eRecursiveDirectoryIterator
. Os últimos têm métodos que os permitem aprofundar, os primeiros não.Quando as instâncias desses iteradores são executadas em loop por conta própria, mesmo os recursivos, os valores só vêm do nível "superior", mesmo que estejam em loop em uma matriz ou diretório aninhado com subdiretórios.
Os iteradores recursivos implementam comportamento recursivo (via
hasChildren()
,getChildren()
) , mas não o exploram .Pode ser melhor pensar nos iteradores recursivos como iteradores "recursíveis", eles têm a capacidade de serem iterados recursivamente, mas simplesmente iterar sobre uma instância de uma dessas classes não fará isso. Para explorar o comportamento recursivo, continue lendo.
RecursiveIteratorIterator
É aqui que
RecursiveIteratorIterator
entra o jogo. Ele tem o conhecimento de como chamar os iteradores "recursíveis" de forma a detalhar a estrutura em um loop normal e plano. Ele coloca o comportamento recursivo em ação. Essencialmente, ele faz o trabalho de passar por cima de cada um dos valores no iterador, procurando ver se há "filhos" para recursar ou não, e entrar e sair dessas coleções de filhos. Você coloca uma instância deRecursiveIteratorIterator
em um foreach, e ele mergulha na estrutura para que você não precise fazer isso.Se o
RecursiveIteratorIterator
não foi usado, você teria que escrever seus próprios loops recursivos para explorar o comportamento recursivo, comparando com o iterador "recursível"hasChildren()
e usandogetChildren()
.Essa é uma breve visão geral de
RecursiveIteratorIterator
como é diferenteIteratorIterator
? Bem, você está basicamente fazendo o mesmo tipo de pergunta que Qual é a diferença entre um gatinho e uma árvore? Só porque ambos aparecem na mesma enciclopédia (ou manual, para os iteradores) não significa que você deva se confundir entre os dois.IteratorIterator
O trabalho do
IteratorIterator
é pegar qualquerTraversable
objeto e envolvê-lo de forma que satisfaça aIterator
interface. Um uso para isso é ser capaz de aplicar o comportamento específico do iterador no objeto não iterador.Para dar um exemplo prático, a
DatePeriod
classe é,Traversable
mas não umIterator
. Como tal, podemos fazer um loop sobre seus valores com,foreach()
mas não podemos fazer outras coisas que normalmente faríamos com um iterador, como a filtragem.TAREFA : Loop ao longo das segundas, quartas e sextas-feiras das próximas quatro semanas.
Sim, isso é trivial por
foreach
-ing noDatePeriod
e usando umif()
dentro do loop; mas esse não é o ponto deste exemplo!O trecho acima não funcionará porque o
CallbackFilterIterator
espera uma instância de uma classe que implementa aIterator
interface, queDatePeriod
não. No entanto, como éTraversable
, podemos facilmente satisfazer esse requisito usandoIteratorIterator
.Como você pode ver, isso não tem nada a ver com a iteração em classes de iteradores nem com a recursão, e é aí que reside a diferença entre
IteratorIterator
eRecursiveIteratorIterator
.Resumo
RecursiveIteraratorIterator
é para iterar sobre umRecursiveIterator
(iterador "recursível"), explorando o comportamento recursivo que está disponível.IteratorIterator
é para aplicar oIterator
comportamento aTraversable
objetos não iterativos .fonte
IteratorIterator
apenas o tipo padrão de passagem de ordem linear paraTraversable
objetos? Aqueles que poderiam ser usados sem ele exatamenteforeach
como estão? E, ainda mais, não éRecursiveIterator
sempre umTraversable
e, portanto, não apenas,IteratorIterator
mas tambémRecursiveIteratorIterator
sempre "para aplicarIterator
comportamento a objetos não iteradores, Traversable" ? (Agora eu diria queforeach
aplica o tipo de iteração por meio do objeto iterador em objetos contêiner que implementam uma interface do tipo iterador, portanto, esses são objetos contêiner-iterador, sempreTraversable
)IteratorIterator
é uma classe que trata de envolverTraversable
objetos em umIterator
. Mais nada . Você parece estar aplicando o termo de forma mais geral.Recursive
inRecursiveIterator
implica comportamento, enquanto um nome mais adequado teria sido aquele que descreve capacidade, comoRecursibleIterator
.Quando usado com
iterator_to_array()
,RecursiveIteratorIterator
percorrerá recursivamente o array para encontrar todos os valores. O que significa que ele irá nivelar a matriz original.IteratorIterator
manterá a estrutura hierárquica original.Este exemplo mostrará claramente a diferença:
fonte
new IteratorIterator(new ArrayIterator($array))
equivale anew ArrayIterator($array)
, isto é, o externo nãoIteratorIterator
está fazendo nada. Além disso, o achatamento da saída não tem nada a ver comiterator_to_array
- ele simplesmente converte o iterador em uma matriz. O achatamento é uma propriedade da maneira comoRecursiveArrayIterator
percorre seu iterador interno.RecursiveDirectoryIterator ele exibe o nome do caminho completo e não apenas o nome do arquivo. O resto parece bom. Isso ocorre porque os nomes dos arquivos são gerados por SplFileInfo. Em vez disso, eles devem ser exibidos como o nome de base. O resultado desejado é o seguinte:
resultado:
fonte