Si pregunta cómo RecursiveIteratorIteratorfunciona, ¿ya ha entendido cómo IteratorIteratorfunciona? Quiero decir que es básicamente lo mismo, solo que la interfaz que consumen los dos es diferente. ¿Está más interesado en algunos ejemplos o desea ver las diferencias de la implementación del código C subyacente?
hakre
@Gordon no estaba seguro de cómo un solo bucle foreach puede atravesar todos los elementos en la estructura del árbol
varuog
@hakra Ahora estoy tratando de estudiar todas las interfaces integradas, así como la implementación de la interfaz spl y el iterador. Estaba interesado en saber cómo funciona en segundo plano con el bucle forach con algunos ejemplos.
varuog
@hakre En realidad, ambos son muy diferentes. IteratorIteratormapas Iteratory IteratorAggregateen un Iterator, donde REcusiveIteratorIteratorse usa para atravesar recusivamente aRecursiveIterator
Adán
Respuestas:
249
RecursiveIteratorIteratores un cruce de árbol deIterator implementación concreto . Permite a un programador atravesar un objeto contenedor que implementa la interfaz, consulteRecursiveIterator Iterador en Wikipedia para conocer los principios generales, tipos, semántica y patrones de los iteradores.
A diferencia de lo IteratorIteratorque es un Iteratorobjeto transversal de implementación concreto en orden lineal (y por defecto acepta cualquier tipo de Traversableen su constructor), RecursiveIteratorIteratorpermite recorrer todos los nodos en un árbol ordenado de objetos y su constructor toma unRecursiveIterator .
En resumen: le RecursiveIteratorIteratorpermite recorrer un árbol, le IteratorIteratorpermite recorrer una lista. Pronto lo muestro con algunos ejemplos de código a continuación.
Técnicamente, esto funciona rompiendo la linealidad atravesando todos los hijos de un nodo (si los hay). Esto es posible porque, por definición, todos los hijos de un nodo son nuevamente a RecursiveIterator. El nivel superior Iteratorluego apila internamente los diferentes RecursiveIterators por su profundidad y mantiene un puntero al sub actual activo Iteratorpara el recorrido.
Esto permite visitar todos los nodos de un árbol.
Los principios subyacentes son los mismos que con IteratorIterator: Una interfaz especifica el tipo de iteración y la clase de iterador base es la implementación de esta semántica. Compare con los ejemplos a continuación, para el bucle lineal con foreachusted normalmente no piense mucho en los detalles de implementación a menos que necesite definir un nuevo Iterator(por ejemplo, cuando algún tipo concreto en sí mismo no se implementaTraversable ).
Para el recorrido recursivo, a menos que no use una Traversaliteración de recorrido recursiva predefinida , normalmente necesita crear una instancia de la RecursiveIteratorIteratoriteración existente o incluso escribir una iteración de recorrido recursiva que sea Traversablesuya para tener este tipo de iteración de recorrido foreach.
Propina: probablemente no implementó uno ni el otro por su cuenta, por lo que esto podría ser algo que valga la pena hacer para su experiencia práctica de las diferencias que tienen. Encontrará una sugerencia de bricolaje al final de la respuesta.
Diferencias técnicas en resumen:
Si bien IteratorIteratortoma cualquiera Traversablepara el recorrido lineal,RecursiveIteratorIterator necesita uno más específico RecursiveIteratorpara recorrer un árbol.
Donde IteratorIteratorexpone su Iteratorvía principal getInnerIerator(),RecursiveIteratorIterator proporciona el sub-activo actual Iteratorsólo a través de ese método.
Si bien IteratorIteratorno tiene conocimiento de nada como padres o hijos,RecursiveIteratorIterator sabe cómo atrapar y atravesar a los niños.
IteratorIterator no necesita una pila de iteradores, RecursiveIteratorIterator tiene dicha pila y conoce el sub-iterador activo.
Donde IteratorIteratortiene su orden debido a la linealidad y no hay opción, RecursiveIteratorIteratortiene una opción para un mayor recorrido y necesita decidir por cada nodo (decidido a través del modo porRecursiveIteratorIterator ).
RecursiveIteratorIteratortiene más métodos que IteratorIterator.
Para resumir: RecursiveIteratores un tipo concreto de iteración (bucle sobre un árbol) que funciona en sus propios iteradores, a saber RecursiveIterator. Ese es el mismo principio subyacente que con IteratorIerator, pero el tipo de iteración es diferente (orden lineal).
Idealmente, también puede crear su propio conjunto. Lo único necesario es que su iterador implemente lo Traversableque es posible a través de Iteratoro IteratorAggregate. Entonces puedes usarlo con foreach. Por ejemplo, algún tipo de objeto de iteración recursiva transversal de árbol ternario junto con la interfaz de iteración correspondiente para los objetos de contenedor.
Repasemos con algunos ejemplos de la vida real que no son tan abstractos. Entre interfaces, iteradores concretos, objetos contenedores y semántica de iteración, esto tal vez no sea una mala idea.
Tome una lista de directorio como ejemplo. Considere que tiene el siguiente árbol de archivos y directorios en el disco:
Mientras que un iterador con orden lineal simplemente atraviesa la carpeta y los archivos de nivel superior (una lista de un solo directorio), el iterador recursivo también atraviesa las subcarpetas y enumera todas las carpetas y archivos (una lista de directorios con listas de sus subdirectorios):
Non-RecursiveRecursive======================[tree][tree]├ dirA ├ dirA
└ fileA │├ dirB
││└ fileD
│├ fileB
│└ fileC
└ fileA
Puede compararlo fácilmente con el IteratorIteratorque no tiene recursividad para recorrer el árbol de directorios. Y elRecursiveIteratorIterator que puede atravesar el árbol como muestra el listado recursivo.
El resultado ejemplar para la estructura de directorios anterior es:
[tree]├.├..├ dirA
├ fileA
Como puede ver, esto aún no está usando IteratorIteratoro RecursiveIteratorIterator. En su lugar, solo usa foreachque opera en elTraversable interfaz.
Como foreachde forma predeterminada solo conoce el tipo de iteración denominado orden lineal, es posible que deseemos especificar el tipo de iteración explícitamente. A primera vista, puede parecer demasiado detallado, pero para fines de demostración (y para hacer la diferencia RecursiveIteratorIteratormás visible más adelante), especifiquemos el tipo lineal de iteración especificando explícitamente el IteratorIteratortipo de iteración para la lista de directorios:
$files =newIteratorIterator($dir);
echo "[$path]\n";foreach($files as $file){
echo " ├ $file\n";}
Este ejemplo es casi idéntico al primero, la diferencia es que $filesahora es un IteratorIteratortipo de iteración para Traversable$dir:
$files =newIteratorIterator($dir);
Como de costumbre, el acto de iteración lo realiza foreach:
foreach($files as $file){
La salida es exactamente la misma. Entonces, ¿qué es diferente? Diferente es el objeto utilizado dentro de foreach. En el primer ejemplo es un DirectoryIteratoren el segundo ejemplo es IteratorIterator. Esto muestra la flexibilidad que tienen los iteradores: puede reemplazarlos entre sí, el código interno foreachsimplemente continúa funcionando como se esperaba.
Comencemos a obtener la lista completa, incluidos los subdirectorios.
Como ahora hemos especificado el tipo de iteración, consideremos cambiarlo a otro tipo de iteración.
Sabemos que debemos atravesar todo el árbol ahora, no solo el primer nivel. Para tener ese trabajo con un simple foreachnecesitamos un tipo diferente de iterador: RecursiveIteratorIterator. Y eso solo puede iterar sobre objetos contenedores que tienen la RecursiveIteratorinterfaz .
La interfaz es un contrato. Cualquier clase que lo implemente se puede usar junto con RecursiveIteratorIterator. Un ejemplo de tal clase es the RecursiveDirectoryIterator, que es algo así como la variante recursiva deDirectoryIterator .
Veamos un primer ejemplo de código antes de escribir cualquier otra oración con la palabra I:
$dir =newRecursiveDirectoryIterator($path);
echo "[$path]\n";foreach($dir as $file){
echo " ├ $file\n";}
Este tercer ejemplo es casi idéntico al primero, sin embargo, crea una salida diferente:
[tree]├ tree\.
├ tree\..├ tree\dirA
├ tree\fileA
De acuerdo, no tan diferente, el nombre del archivo ahora contiene el nombre de la ruta al frente, pero el resto también se ve similar.
Como muestra el ejemplo, incluso el objeto de directorio ya implementa la RecursiveIteratorinterfaz, esto aún no es suficiente para foreachrecorrer todo el árbol de directorios. Aquí es donde RecursiveIteratorIteratorentra en acción. El ejemplo 4 muestra cómo:
$files =newRecursiveIteratorIterator($dir);
echo "[$path]\n";foreach($files as $file){
echo " ├ $file\n";}
Usar el en RecursiveIteratorIteratorlugar de solo el $dirobjeto anterior hará que se foreachrecorran todos los archivos y directorios de forma recursiva. Esto luego enumera todos los archivos, ya que ahora se ha especificado el tipo de iteración del objeto:
Esto ya debería demostrar la diferencia entre recorrido plano y árbol. El RecursiveIteratorIteratorpuede atravesar cualquier estructura en forma de árbol como una lista de elementos. Debido a que hay más información (como el nivel en el que tiene lugar actualmente la iteración), es posible acceder al objeto iterador mientras se itera sobre él y, por ejemplo, sangrar la salida:
Seguro que esto no gana un concurso de belleza, pero muestra que con el iterador recursivo hay más información disponible que solo el orden lineal de clave y valor . Incluso foreachsolo se puede expresar este tipo de linealidad, acceder al propio iterador permite obtener más información.
De manera similar a la metainformación, también hay diferentes formas posibles de atravesar el árbol y, por lo tanto, ordenar la salida. Este es el modo deRecursiveIteratorIterator y se puede configurar con el constructor.
El siguiente ejemplo le dirá a la RecursiveDirectoryIteratorque elimine las entradas de puntos ( .y ..) ya que no las necesitamos. Pero también se cambiará el modo de recursividad para tomar el elemento padre (el subdirectorio) primero ( SELF_FIRST) antes que los hijos (los archivos y subdirectorios del subdirectorio):
Cuando compara eso con el recorrido estándar, todas estas cosas no están disponibles. Por lo tanto, la iteración recursiva es un poco más compleja cuando necesitas entenderla, sin embargo, es fácil de usar porque se comporta como un iterador, lo pones en ay foreachlisto.
Creo que estos son ejemplos suficientes para una respuesta. Puede encontrar el código fuente completo, así como un ejemplo para mostrar árboles ascii de buen aspecto en esta esencia: https://gist.github.com/3599532
Hágalo usted mismo: haga el RecursiveTreeIteratortrabajo línea por línea.
El ejemplo 5 demostró que hay metainformación disponible sobre el estado del iterador. Sin embargo, esto se demostró a propósito dentro de la foreachiteración. En la vida real, esto pertenece naturalmente al interior delRecursiveIterator .
Un mejor ejemplo es el RecursiveTreeIterator, se encarga de sangrar, prefijar, etc. Vea el siguiente fragmento de código:
Cuando se usa en combinación con un RecursiveDirectoryIterator, muestra el nombre completo de la ruta y no solo el nombre del archivo. El resto luce bien. Esto se debe a que los nombres de archivo son generados por SplFileInfo. En su lugar, deben mostrarse como el nombre de base. La salida deseada es la siguiente:
Cree una clase de decorador que se pueda usar con en RecursiveTreeIteratorlugar de RecursiveDirectoryIterator. Debe proporcionar el nombre base de la actual en SplFileInfolugar del nombre de la ruta. El fragmento de código final podría verse así:
Estos fragmentos incluidos $unicodeTreePrefixson parte de la esencia del Apéndice: Hágalo usted mismo: Haga el RecursiveTreeIteratortrabajo línea por línea. .
No responde a las preguntas planteadas, contiene errores fácticos y pierde puntos centrales cuando pasa a personalizar la iteración. Con todo, parece un mal intento de obtener la recompensa por un tema del que no sabe mucho o, si lo sabe, no puede resumir en una respuesta a las preguntas planteadas.
salathe
2
Bueno, lo que no responde a mi pregunta de "por qué", simplemente crea más palabras sin decir mucho. ¿Quizás empieces con un error que cuenta? Apúntelo, no lo mantenga en secreto.
hakre
La primera oración es incorrecta: "RecursiveIteratorIterator es un IteratorIterator que admite ...", esto no es cierto.
salathe
1
@salathe: Tanque para sus comentarios. Edité la respuesta para abordarla. De hecho, la primera oración era incorrecta e incompleta. Todavía he omitido los detalles concretos de implementación RecursiveIteratorIteratorporque esto es común con otros tipos, pero proporcioné información técnica sobre cómo funciona realmente. Creo que los ejemplos muestran bien las diferencias: el tipo de iteración es la principal diferencia entre los dos. No tengo idea de que si compras el tipo de iteración, lo acuñas de manera un poco diferente, pero en mi humilde opinión no es fácil con la semántica que tienen los tipos de iteración.
Hakre
1
La primera parte se ha mejorado un poco, pero una vez que comienza a pasar a los ejemplos, todavía se desvían hacia inexactitudes fácticas. Si recortara la respuesta en la regla horizontal, se mejoraría mucho.
salathe
31
¿Cuál es la diferencia de IteratorIteratory RecursiveIteratorIterator?
Para comprender la diferencia entre estos dos iteradores, primero se debe comprender un poco acerca de las convenciones de nomenclatura utilizadas y lo que entendemos por iteradores "recursivos".
Iteradores recursivos y no recursivos
PHP tiene iteradores no "recursivos", como ArrayIteratory FilesystemIterator. También hay iteradores "recursivos" como RecursiveArrayIteratory RecursiveDirectoryIterator. Los últimos tienen métodos que les permiten profundizar, los primeros no.
Cuando las instancias de estos iteradores se repiten por sí solas, incluso las recursivas, los valores solo provienen del nivel "superior" incluso si se repiten en una matriz anidada o un directorio con subdirectorios.
Los iteradores recursivos implementan un comportamiento recursivo (vía hasChildren(), getChildren()) pero no lo explotan .
Podría ser mejor pensar en los iteradores recursivos como iteradores "recurrentes", tienen la capacidad de ser iterados de manera recursiva pero simplemente iterar sobre una instancia de una de estas clases no hará eso. Para explotar el comportamiento recursivo, sigue leyendo.
RecursiveIteratorIterator
Aquí es donde RecursiveIteratorIteratorentra en juego. Tiene el conocimiento de cómo llamar a los iteradores "recurrentes" de tal manera que profundice en la estructura en un bucle normal y plano. Pone en acción el comportamiento recursivo. Básicamente hace el trabajo de pasar por encima de cada uno de los valores en el iterador, buscando ver si hay "niños" en los que recurrir o no, y entrar y salir de esas colecciones de niños. Pegas una instancia de RecursiveIteratorIteratoren un foreach y se sumerge en la estructura para que tú no tengas que hacerlo.
Si RecursiveIteratorIteratorno se usó, tendría que escribir sus propios bucles recursivos para explotar el comportamiento recursivo, verificando el iterador "recurrente" hasChildren()y usando getChildren().
Así que esa es una breve descripción general de RecursiveIteratorIterator, ¿en qué se diferencia IteratorIterator? Bueno, básicamente estás haciendo el mismo tipo de pregunta que ¿Cuál es la diferencia entre un gatito y un árbol? El hecho de que ambos aparezcan en la misma enciclopedia (o manual, para los iteradores) no significa que deba confundirse entre los dos.
IteradorIterador
El trabajo del IteratorIteratores tomar cualquier Traversableobjeto y envolverlo de manera que satisfaga la Iteratorinterfaz. Un uso de esto es poder aplicar el comportamiento específico del iterador en el objeto no iterador.
Para dar un ejemplo práctico, la DatePeriodclase es Traversablepero no un Iterator. Como tal, podemos recorrer sus valores con foreach()pero no podemos hacer otras cosas que normalmente haríamos con un iterador, como filtrar.
TAREA : Repita los lunes, miércoles y viernes de las próximas cuatro semanas.
Sí, esto es trivial al foreachpasar sobre el DatePeriody usar un if()dentro del ciclo; ¡pero ese no es el objetivo de este ejemplo!
$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){…}
El fragmento de código anterior no funcionará porque CallbackFilterIteratorespera una instancia de una clase que implemente la Iteratorinterfaz, que DatePeriodno lo hace. Sin embargo, dado que es Traversable, podemos satisfacer fácilmente ese requisito utilizando IteratorIterator.
$period =newIteratorIterator(newDatePeriod(…));
Como puede ver, esto no tiene nada que ver con iterar sobre clases de iterador ni recursividad, y ahí radica la diferencia entre IteratorIteratory RecursiveIteratorIterator.
Resumen
RecursiveIteraratorIteratores para iterar sobre un RecursiveIterator(iterador "recurrente"), explotando el comportamiento recursivo que está disponible.
IteratorIteratores para aplicar Iteratorcomportamiento a Traversableobjetos que no son iteradores .
¿No es IteratorIteratorsolo el tipo estándar de recorrido de orden lineal para Traversableobjetos? ¿Aquellos que podrían usarse sin él tal cual foreach? Y aún más, ¿no es RecursiveIteratorsiempre un Traversabley por lo tanto no solo IteratorIteratorsino también RecursiveIteratorIteratorsiempre "para aplicar Iteratorcomportamiento a objetos no iteradores, Traversables" ? (Ahora diría que foreachaplica el tipo de iteración a través del objeto iterador en objetos contenedor que implementan una interfaz de tipo iterador, por lo que estos son objetos contenedor-iterador, siempre Traversable)
hakre
Como dice mi respuesta, IteratorIteratores una clase que se trata de envolver Traversableobjetos en un Iterator. Nada más . Parece que está aplicando el término de manera más general.
salathe
Respuesta aparentemente informativa. Una pregunta, ¿no RecursiveIteratorIterator también envolvería los objetos para que también tuvieran acceso al comportamiento de Iterator? La única diferencia entre los dos sería que RecursiveIteratorIterator puede profundizar, mientras que IteratorIterator no puede.
Mike Purcell
@salathe, ¿sabe por qué el iterador recursivo (RecursiveDirectoryIterator) no implementa el comportamiento hasChildren (), getChildren ()?
anru
7
+1 por decir "recurrente". El nombre me extravió durante mucho tiempo porque Recursiveen RecursiveIteratorimplica comportamiento, mientras que un nombre más adecuado hubiera sido uno que describa la capacidad, como RecursibleIterator.
cabra
0
Cuando se usa con iterator_to_array(), RecursiveIteratorIteratorrecorrerá recursivamente la matriz para encontrar todos los valores. Lo que significa que aplanará la matriz original.
IteratorIterator mantendrá la estructura jerárquica original.
Este ejemplo le mostrará claramente la diferencia:
Esto es totalmente engañoso. new IteratorIterator(new ArrayIterator($array))es equivalente a new ArrayIterator($array), es decir, lo externo IteratorIteratorno hace nada. Además, el aplanamiento de la salida no tiene nada que ver con iterator_to_arrayeso, simplemente convierte el iterador en una matriz. El aplanamiento es una propiedad de la forma en que RecursiveArrayIteratorcamina su iterador interno.
Preguntas de Quolonel
0
RecursiveDirectoryIterator muestra el nombre completo de la ruta y no solo el nombre del archivo. El resto luce bien. Esto se debe a que los nombres de archivo son generados por SplFileInfo. En su lugar, deben mostrarse como el nombre de base. La salida deseada es la siguiente:
RecursiveIteratorIterator
funciona, ¿ya ha entendido cómoIteratorIterator
funciona? Quiero decir que es básicamente lo mismo, solo que la interfaz que consumen los dos es diferente. ¿Está más interesado en algunos ejemplos o desea ver las diferencias de la implementación del código C subyacente?IteratorIterator
mapasIterator
yIteratorAggregate
en unIterator
, dondeREcusiveIteratorIterator
se usa para atravesar recusivamente aRecursiveIterator
Respuestas:
RecursiveIteratorIterator
es un cruce de árbol deIterator
implementación concreto . Permite a un programador atravesar un objeto contenedor que implementa la interfaz, consulteRecursiveIterator
Iterador en Wikipedia para conocer los principios generales, tipos, semántica y patrones de los iteradores.A diferencia de lo
IteratorIterator
que es unIterator
objeto transversal de implementación concreto en orden lineal (y por defecto acepta cualquier tipo deTraversable
en su constructor),RecursiveIteratorIterator
permite recorrer todos los nodos en un árbol ordenado de objetos y su constructor toma unRecursiveIterator
.En resumen: le
RecursiveIteratorIterator
permite recorrer un árbol, leIteratorIterator
permite recorrer una lista. Pronto lo muestro con algunos ejemplos de código a continuación.Técnicamente, esto funciona rompiendo la linealidad atravesando todos los hijos de un nodo (si los hay). Esto es posible porque, por definición, todos los hijos de un nodo son nuevamente a
RecursiveIterator
. El nivel superiorIterator
luego apila internamente los diferentesRecursiveIterator
s por su profundidad y mantiene un puntero al sub actual activoIterator
para el recorrido.Esto permite visitar todos los nodos de un árbol.
Los principios subyacentes son los mismos que con
IteratorIterator
: Una interfaz especifica el tipo de iteración y la clase de iterador base es la implementación de esta semántica. Compare con los ejemplos a continuación, para el bucle lineal conforeach
usted normalmente no piense mucho en los detalles de implementación a menos que necesite definir un nuevoIterator
(por ejemplo, cuando algún tipo concreto en sí mismo no se implementaTraversable
).Para el recorrido recursivo, a menos que no use una
Traversal
iteración de recorrido recursiva predefinida , normalmente necesita crear una instancia de laRecursiveIteratorIterator
iteración existente o incluso escribir una iteración de recorrido recursiva que seaTraversable
suya para tener este tipo de iteración de recorridoforeach
.Diferencias técnicas en resumen:
IteratorIterator
toma cualquieraTraversable
para el recorrido lineal,RecursiveIteratorIterator
necesita uno más específicoRecursiveIterator
para recorrer un árbol.IteratorIterator
expone suIterator
vía principalgetInnerIerator()
,RecursiveIteratorIterator
proporciona el sub-activo actualIterator
sólo a través de ese método.IteratorIterator
no tiene conocimiento de nada como padres o hijos,RecursiveIteratorIterator
sabe cómo atrapar y atravesar a los niños.IteratorIterator
no necesita una pila de iteradores,RecursiveIteratorIterator
tiene dicha pila y conoce el sub-iterador activo.IteratorIterator
tiene su orden debido a la linealidad y no hay opción,RecursiveIteratorIterator
tiene una opción para un mayor recorrido y necesita decidir por cada nodo (decidido a través del modo porRecursiveIteratorIterator
).RecursiveIteratorIterator
tiene más métodos queIteratorIterator
.Para resumir:
RecursiveIterator
es un tipo concreto de iteración (bucle sobre un árbol) que funciona en sus propios iteradores, a saberRecursiveIterator
. Ese es el mismo principio subyacente que conIteratorIerator
, pero el tipo de iteración es diferente (orden lineal).Idealmente, también puede crear su propio conjunto. Lo único necesario es que su iterador implemente lo
Traversable
que es posible a través deIterator
oIteratorAggregate
. Entonces puedes usarlo conforeach
. Por ejemplo, algún tipo de objeto de iteración recursiva transversal de árbol ternario junto con la interfaz de iteración correspondiente para los objetos de contenedor.Repasemos con algunos ejemplos de la vida real que no son tan abstractos. Entre interfaces, iteradores concretos, objetos contenedores y semántica de iteración, esto tal vez no sea una mala idea.
Tome una lista de directorio como ejemplo. Considere que tiene el siguiente árbol de archivos y directorios en el disco:
Mientras que un iterador con orden lineal simplemente atraviesa la carpeta y los archivos de nivel superior (una lista de un solo directorio), el iterador recursivo también atraviesa las subcarpetas y enumera todas las carpetas y archivos (una lista de directorios con listas de sus subdirectorios):
Puede compararlo fácilmente con el
IteratorIterator
que no tiene recursividad para recorrer el árbol de directorios. Y elRecursiveIteratorIterator
que puede atravesar el árbol como muestra el listado recursivo.En un primer momento un ejemplo muy básico, con una
DirectoryIterator
que implementaTraversable
lo que permiteforeach
a iterar sobre ella:El resultado ejemplar para la estructura de directorios anterior es:
Como puede ver, esto aún no está usando
IteratorIterator
oRecursiveIteratorIterator
. En su lugar, solo usaforeach
que opera en elTraversable
interfaz.Como
foreach
de forma predeterminada solo conoce el tipo de iteración denominado orden lineal, es posible que deseemos especificar el tipo de iteración explícitamente. A primera vista, puede parecer demasiado detallado, pero para fines de demostración (y para hacer la diferenciaRecursiveIteratorIterator
más visible más adelante), especifiquemos el tipo lineal de iteración especificando explícitamente elIteratorIterator
tipo de iteración para la lista de directorios:Este ejemplo es casi idéntico al primero, la diferencia es que
$files
ahora es unIteratorIterator
tipo de iteración paraTraversable
$dir
:Como de costumbre, el acto de iteración lo realiza
foreach
:La salida es exactamente la misma. Entonces, ¿qué es diferente? Diferente es el objeto utilizado dentro de
foreach
. En el primer ejemplo es unDirectoryIterator
en el segundo ejemplo esIteratorIterator
. Esto muestra la flexibilidad que tienen los iteradores: puede reemplazarlos entre sí, el código internoforeach
simplemente continúa funcionando como se esperaba.Comencemos a obtener la lista completa, incluidos los subdirectorios.
Como ahora hemos especificado el tipo de iteración, consideremos cambiarlo a otro tipo de iteración.
Sabemos que debemos atravesar todo el árbol ahora, no solo el primer nivel. Para tener ese trabajo con un simple
foreach
necesitamos un tipo diferente de iterador:RecursiveIteratorIterator
. Y eso solo puede iterar sobre objetos contenedores que tienen laRecursiveIterator
interfaz .La interfaz es un contrato. Cualquier clase que lo implemente se puede usar junto con
RecursiveIteratorIterator
. Un ejemplo de tal clase es theRecursiveDirectoryIterator
, que es algo así como la variante recursiva deDirectoryIterator
.Veamos un primer ejemplo de código antes de escribir cualquier otra oración con la palabra I:
Este tercer ejemplo es casi idéntico al primero, sin embargo, crea una salida diferente:
De acuerdo, no tan diferente, el nombre del archivo ahora contiene el nombre de la ruta al frente, pero el resto también se ve similar.
Como muestra el ejemplo, incluso el objeto de directorio ya implementa la
RecursiveIterator
interfaz, esto aún no es suficiente paraforeach
recorrer todo el árbol de directorios. Aquí es dondeRecursiveIteratorIterator
entra en acción. El ejemplo 4 muestra cómo:Usar el en
RecursiveIteratorIterator
lugar de solo el$dir
objeto anterior hará que seforeach
recorran todos los archivos y directorios de forma recursiva. Esto luego enumera todos los archivos, ya que ahora se ha especificado el tipo de iteración del objeto:Esto ya debería demostrar la diferencia entre recorrido plano y árbol. El
RecursiveIteratorIterator
puede atravesar cualquier estructura en forma de árbol como una lista de elementos. Debido a que hay más información (como el nivel en el que tiene lugar actualmente la iteración), es posible acceder al objeto iterador mientras se itera sobre él y, por ejemplo, sangrar la salida:Y salida del Ejemplo 5 :
Seguro que esto no gana un concurso de belleza, pero muestra que con el iterador recursivo hay más información disponible que solo el orden lineal de clave y valor . Incluso
foreach
solo se puede expresar este tipo de linealidad, acceder al propio iterador permite obtener más información.De manera similar a la metainformación, también hay diferentes formas posibles de atravesar el árbol y, por lo tanto, ordenar la salida. Este es el modo de
RecursiveIteratorIterator
y se puede configurar con el constructor.El siguiente ejemplo le dirá a la
RecursiveDirectoryIterator
que elimine las entradas de puntos (.
y..
) ya que no las necesitamos. Pero también se cambiará el modo de recursividad para tomar el elemento padre (el subdirectorio) primero (SELF_FIRST
) antes que los hijos (los archivos y subdirectorios del subdirectorio):La salida ahora muestra las entradas del subdirectorio correctamente enumeradas, si compara con la salida anterior, las que no estaban allí:
Por lo tanto, el modo de recursividad controla qué y cuándo se devuelve una rama u hoja en el árbol, para el ejemplo del directorio:
LEAVES_ONLY
(predeterminado): solo enumera archivos, no directorios.SELF_FIRST
(arriba): lista el directorio y luego los archivos allí.CHILD_FIRST
(sin ejemplo): enumere los archivos en el subdirectorio primero y luego en el directorio.Salida del ejemplo 5 con los otros dos modos:
Cuando compara eso con el recorrido estándar, todas estas cosas no están disponibles. Por lo tanto, la iteración recursiva es un poco más compleja cuando necesitas entenderla, sin embargo, es fácil de usar porque se comporta como un iterador, lo pones en ay
foreach
listo.Creo que estos son ejemplos suficientes para una respuesta. Puede encontrar el código fuente completo, así como un ejemplo para mostrar árboles ascii de buen aspecto en esta esencia: https://gist.github.com/3599532
El ejemplo 5 demostró que hay metainformación disponible sobre el estado del iterador. Sin embargo, esto se demostró a propósito dentro de la
foreach
iteración. En la vida real, esto pertenece naturalmente al interior delRecursiveIterator
.Un mejor ejemplo es el
RecursiveTreeIterator
, se encarga de sangrar, prefijar, etc. Vea el siguiente fragmento de código:El
RecursiveTreeIterator
está destinado a la línea de trabajo de la línea, la salida es bastante sencillo con un pequeño problema:Cuando se usa en combinación con un
RecursiveDirectoryIterator
, muestra el nombre completo de la ruta y no solo el nombre del archivo. El resto luce bien. Esto se debe a que los nombres de archivo son generados porSplFileInfo
. En su lugar, deben mostrarse como el nombre de base. La salida deseada es la siguiente:Cree una clase de decorador que se pueda usar con en
RecursiveTreeIterator
lugar deRecursiveDirectoryIterator
. Debe proporcionar el nombre base de la actual enSplFileInfo
lugar del nombre de la ruta. El fragmento de código final podría verse así:Estos fragmentos incluidos
$unicodeTreePrefix
son parte de la esencia del Apéndice: Hágalo usted mismo: Haga elRecursiveTreeIterator
trabajo línea por línea. .fuente
RecursiveIteratorIterator
porque esto es común con otros tipos, pero proporcioné información técnica sobre cómo funciona realmente. Creo que los ejemplos muestran bien las diferencias: el tipo de iteración es la principal diferencia entre los dos. No tengo idea de que si compras el tipo de iteración, lo acuñas de manera un poco diferente, pero en mi humilde opinión no es fácil con la semántica que tienen los tipos de iteración.Para comprender la diferencia entre estos dos iteradores, primero se debe comprender un poco acerca de las convenciones de nomenclatura utilizadas y lo que entendemos por iteradores "recursivos".
Iteradores recursivos y no recursivos
PHP tiene iteradores no "recursivos", como
ArrayIterator
yFilesystemIterator
. También hay iteradores "recursivos" comoRecursiveArrayIterator
yRecursiveDirectoryIterator
. Los últimos tienen métodos que les permiten profundizar, los primeros no.Cuando las instancias de estos iteradores se repiten por sí solas, incluso las recursivas, los valores solo provienen del nivel "superior" incluso si se repiten en una matriz anidada o un directorio con subdirectorios.
Los iteradores recursivos implementan un comportamiento recursivo (vía
hasChildren()
,getChildren()
) pero no lo explotan .Podría ser mejor pensar en los iteradores recursivos como iteradores "recurrentes", tienen la capacidad de ser iterados de manera recursiva pero simplemente iterar sobre una instancia de una de estas clases no hará eso. Para explotar el comportamiento recursivo, sigue leyendo.
RecursiveIteratorIterator
Aquí es donde
RecursiveIteratorIterator
entra en juego. Tiene el conocimiento de cómo llamar a los iteradores "recurrentes" de tal manera que profundice en la estructura en un bucle normal y plano. Pone en acción el comportamiento recursivo. Básicamente hace el trabajo de pasar por encima de cada uno de los valores en el iterador, buscando ver si hay "niños" en los que recurrir o no, y entrar y salir de esas colecciones de niños. Pegas una instancia deRecursiveIteratorIterator
en un foreach y se sumerge en la estructura para que tú no tengas que hacerlo.Si
RecursiveIteratorIterator
no se usó, tendría que escribir sus propios bucles recursivos para explotar el comportamiento recursivo, verificando el iterador "recurrente"hasChildren()
y usandogetChildren()
.Así que esa es una breve descripción general de
RecursiveIteratorIterator
, ¿en qué se diferenciaIteratorIterator
? Bueno, básicamente estás haciendo el mismo tipo de pregunta que ¿Cuál es la diferencia entre un gatito y un árbol? El hecho de que ambos aparezcan en la misma enciclopedia (o manual, para los iteradores) no significa que deba confundirse entre los dos.IteradorIterador
El trabajo del
IteratorIterator
es tomar cualquierTraversable
objeto y envolverlo de manera que satisfaga laIterator
interfaz. Un uso de esto es poder aplicar el comportamiento específico del iterador en el objeto no iterador.Para dar un ejemplo práctico, la
DatePeriod
clase esTraversable
pero no unIterator
. Como tal, podemos recorrer sus valores conforeach()
pero no podemos hacer otras cosas que normalmente haríamos con un iterador, como filtrar.TAREA : Repita los lunes, miércoles y viernes de las próximas cuatro semanas.
Sí, esto es trivial al
foreach
pasar sobre elDatePeriod
y usar unif()
dentro del ciclo; ¡pero ese no es el objetivo de este ejemplo!El fragmento de código anterior no funcionará porque
CallbackFilterIterator
espera una instancia de una clase que implemente laIterator
interfaz, queDatePeriod
no lo hace. Sin embargo, dado que esTraversable
, podemos satisfacer fácilmente ese requisito utilizandoIteratorIterator
.Como puede ver, esto no tiene nada que ver con iterar sobre clases de iterador ni recursividad, y ahí radica la diferencia entre
IteratorIterator
yRecursiveIteratorIterator
.Resumen
RecursiveIteraratorIterator
es para iterar sobre unRecursiveIterator
(iterador "recurrente"), explotando el comportamiento recursivo que está disponible.IteratorIterator
es para aplicarIterator
comportamiento aTraversable
objetos que no son iteradores .fuente
IteratorIterator
solo el tipo estándar de recorrido de orden lineal paraTraversable
objetos? ¿Aquellos que podrían usarse sin él tal cualforeach
? Y aún más, ¿no esRecursiveIterator
siempre unTraversable
y por lo tanto no soloIteratorIterator
sino tambiénRecursiveIteratorIterator
siempre "para aplicarIterator
comportamiento a objetos no iteradores, Traversables" ? (Ahora diría queforeach
aplica el tipo de iteración a través del objeto iterador en objetos contenedor que implementan una interfaz de tipo iterador, por lo que estos son objetos contenedor-iterador, siempreTraversable
)IteratorIterator
es una clase que se trata de envolverTraversable
objetos en unIterator
. Nada más . Parece que está aplicando el término de manera más general.Recursive
enRecursiveIterator
implica comportamiento, mientras que un nombre más adecuado hubiera sido uno que describa la capacidad, comoRecursibleIterator
.Cuando se usa con
iterator_to_array()
,RecursiveIteratorIterator
recorrerá recursivamente la matriz para encontrar todos los valores. Lo que significa que aplanará la matriz original.IteratorIterator
mantendrá la estructura jerárquica original.Este ejemplo le mostrará claramente la diferencia:
fuente
new IteratorIterator(new ArrayIterator($array))
es equivalente anew ArrayIterator($array)
, es decir, lo externoIteratorIterator
no hace nada. Además, el aplanamiento de la salida no tiene nada que ver coniterator_to_array
eso, simplemente convierte el iterador en una matriz. El aplanamiento es una propiedad de la forma en queRecursiveArrayIterator
camina su iterador interno.RecursiveDirectoryIterator muestra el nombre completo de la ruta y no solo el nombre del archivo. El resto luce bien. Esto se debe a que los nombres de archivo son generados por SplFileInfo. En su lugar, deben mostrarse como el nombre de base. La salida deseada es la siguiente:
salida:
fuente