En programación informática , un iterador es un objeto que proporciona acceso progresivo a cada elemento de una colección , en orden. [ 1 ] [ 2 ] [ 3 ]
Una colección puede proporcionar varios iteradores a través de su interfaz que ofrecen elementos en diferentes órdenes, como hacia adelante y hacia atrás.
Un iterador a menudo se implementa en términos de la estructura subyacente a la implementación de una colección y suele estar estrechamente acoplado a la colección para habilitar la semántica operativa del iterador.
Un iterador tiene un comportamiento similar al de un cursor de base de datos .
Los iteradores se remontan al lenguaje de programación CLU en 1974. [ 4 ]
Patrón
Un iterador proporciona acceso a un elemento de una colección ( acceso a elemento ) y puede cambiar su estado interno para proporcionar acceso al siguiente elemento ( recorrido de elemento ). [ 5 ] También permite la creación e inicialización de un primer elemento e indica si se han recorrido todos los elementos. En algunos contextos de programación, un iterador proporciona funcionalidad adicional.
Un iterador permite que un consumidor procese cada elemento de una colección, aislándolo de la estructura interna de la misma. [ 2 ] La colección puede almacenar elementos de cualquier forma, mientras que el consumidor puede acceder a ellos como una secuencia.
En la programación orientada a objetos, una clase iteradora suele diseñarse en estrecha coordinación con la clase de colección correspondiente. Normalmente, la colección proporciona los métodos para crear iteradores.
A veces, un contador de bucle también se denomina iterador de bucle. Sin embargo, un contador de bucle solo proporciona la funcionalidad de recorrido y no la de acceso a los elementos.
Generador
Una forma de implementar un iterador es mediante una forma restringida de corrutina , conocida como generador . A diferencia de una subrutina , una corrutina generadora puede devolver valores a quien la llama varias veces, en lugar de devolverlos solo una vez. La mayoría de los iteradores se pueden expresar naturalmente como generadores, pero debido a que estos conservan su estado local entre invocaciones, son particularmente adecuados para iteradores complejos con estado, como los recolectores de árboles . Existen sutiles diferencias y distinciones en el uso de los términos "generador" e "iterador", que varían según los autores y los lenguajes. [ 6 ] En Python , un generador es un constructor de iterador : una función que devuelve un iterador. A continuación se muestra un ejemplo de un generador de Python que devuelve un iterador para los números de Fibonacci utilizando yieldla instrucción de Python:
desde typing import Generatordef fibonacci ( límite : int ) -> Generador [ int , Ninguno , Ninguno ]: a , b = 0 , 1 for _ in rango ( límite ): yield a a , b = b , a + bpara cada número en fibonacci ( 100 ): # El generador construye un iterador print ( número )iterador interno
Un iterador interno es una función de orden superior (que a menudo acepta funciones anónimas ) que recorre una colección aplicando una función a cada elemento. Por ejemplo, mapla función de Python aplica una función definida por quien la llama a cada elemento:
from typing import Iteratordígitos : lista [ int ] = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]dígitos_cuadrados : Iterador [ int ] = map ( lambda x : x ** 2 , dígitos ) # Iterar sobre este iterador daría como resultado 0, 1, 4, 9, 16, ..., 81.Iterador implícito
Algunos lenguajes orientados a objetos como C# , C++ (versiones posteriores), Delphi (versiones posteriores), Go , Java (versiones posteriores), Lua , Perl , Python y Ruby proporcionan una forma intrínseca de iterar a través de los elementos de una colección sin un iterador explícito. Puede existir un objeto iterador, pero no está representado en el código fuente. [ 5 ] [ 7 ]
Un iterador implícito se manifiesta a menudo en la sintaxis del lenguaje como foreach.
En Python, un objeto de colección se puede iterar directamente:
para cada valor en el iterable : imprimir ( valor )En Ruby, la iteración requiere acceder a una propiedad de iterador:
iterable . cada hacer | valor | pone valor finEste estilo de iteración a veces se denomina "iteración interna" porque su código se ejecuta completamente dentro del contexto del objeto iterable (que controla todos los aspectos de la iteración), y el programador solo proporciona la operación a ejecutar en cada paso (utilizando una función anónima ).
Los lenguajes que admiten comprensiones de listas o construcciones similares también pueden utilizar iteradores implícitos durante la construcción de la lista de resultados, como en Python:
nombres : lista [ str ] = [ persona.nombre para persona en lista si persona.masculino ]A veces, la naturaleza implícita y oculta es solo parcial. El lenguaje C++ tiene algunas plantillas de funciones para la iteración implícita, como for_each(). Estas funciones aún requieren objetos iteradores explícitos como entrada inicial, pero la iteración posterior no expone un objeto iterador al usuario.
Arroyo
Los iteradores son una abstracción útil de los flujos de entrada : proporcionan un objeto potencialmente infinito iterable (pero no necesariamente indexable). Varios lenguajes, como Perl y Python, implementan flujos como iteradores. En Python, los iteradores son objetos que representan flujos de datos. [ 8 ] Otras implementaciones de flujos incluyen lenguajes orientados a datos , como AWK y sed .
Contraste con la indexación
En lugar de usar un iterador, muchos lenguajes permiten el uso de un operador de subíndice y un contador de bucle para acceder a cada elemento. Aunque se puede usar la indexación con colecciones, el uso de iteradores puede tener ventajas como: [ 9 ]
- Los bucles de conteo no son adecuados para todas las estructuras de datos, en particular para aquellas con acceso aleatorio nulo o lento , como listas o árboles .
- Los iteradores pueden proporcionar una forma consistente de iterar sobre estructuras de datos de todo tipo y, por lo tanto, hacen que el código sea más legible, reutilizable y menos sensible a un cambio en la estructura de datos .
- Un iterador puede imponer restricciones de acceso adicionales, como garantizar que no se puedan omitir elementos o que no se pueda acceder por segunda vez a un elemento visitado previamente.
- Un iterador permite modificar el objeto de colección sin invalidarlo. Por ejemplo, una vez que el iterador ha avanzado más allá del primer elemento, es posible insertar elementos adicionales al principio de la colección con resultados predecibles. Con la indexación, esto resulta problemático, ya que los números de índice deben cambiar.
La capacidad de modificar una colección mientras se itera sobre sus elementos se ha vuelto indispensable en la programación orientada a objetos moderna , donde las interrelaciones entre objetos y los efectos de las operaciones pueden no ser evidentes. Al usar un iterador, se evitan estas consecuencias. Sin embargo, esta afirmación debe tomarse con cautela, ya que, por razones de eficiencia, la implementación del iterador suele estar tan ligada a la colección que impide la modificación de la colección subyacente sin invalidarse a sí misma.
Para las colecciones que pueden mover sus datos en memoria, la única forma de no invalidar el iterador es que la colección mantenga un registro de todos los iteradores activos y los actualice sobre la marcha. Dado que el número de iteradores en un momento dado puede ser arbitrariamente grande en comparación con el tamaño de la colección vinculada, actualizarlos todos afectaría drásticamente la garantía de complejidad de las operaciones de la colección.
Una forma alternativa de mantener el número de actualizaciones vinculado al tamaño de la colección sería utilizar un mecanismo de identificadores, es decir, una colección de punteros indirectos a los elementos de la colección que deben actualizarse junto con ella, y permitir que los iteradores apunten a estos identificadores en lugar de directamente a los elementos de datos . Sin embargo, este enfoque afectará negativamente al rendimiento del iterador, ya que debe realizar un seguimiento de doble puntero para acceder al elemento de datos real. Esto generalmente no es deseable, porque muchos algoritmos que utilizan iteradores invocan la operación de acceso a datos de los iteradores con más frecuencia que el método de avance. Por lo tanto, es especialmente importante contar con iteradores con un acceso a datos muy eficiente.
En definitiva, siempre se trata de un compromiso entre seguridad (los iteradores permanecen siempre válidos) y eficiencia. La mayoría de las veces, la seguridad adicional no compensa la pérdida de eficiencia. Si se requiere estabilidad para los iteradores, sería mejor usar una colección alternativa (por ejemplo, una lista enlazada simple en lugar de un vector) (y, en general, más eficiente).
Clasificación
Categorías
Los iteradores se pueden clasificar según su funcionalidad. Aquí hay una lista (no exhaustiva) de categorías de iteradores: [ 10 ] [ 11 ]
Tipos
Los distintos lenguajes o bibliotecas utilizados con estos lenguajes definen tipos de iteradores. Algunos de ellos son [ 14 ].
En diferentes lenguajes de programación
.NETO
Los iteradores en .NET Framework (es decir, C#) se llaman "enumeradores" y están representados por la IEnumeratorinterfaz. [ 17 ] : 189–190, 344 [ 18 ] : 53–54IEnumerator proporciona un MoveNext()método, que avanza al siguiente elemento e indica si se ha alcanzado el final de la colección; [ 17 ] : 344 [ 18 ] : 55–56 [ 19 ] : 89 una Currentpropiedad, para obtener el valor del elemento al que apunta actualmente; [ 17 ] : 344 [ 18 ] : 56 [ 19 ] : 89 y un Reset()método opcional, [ 17 ] : 344 para rebobinar el enumerador a su posición inicial. El enumerador apunta inicialmente a un valor especial antes del primer elemento, por lo que MoveNext()se requiere una llamada a para comenzar a iterar.
Los enumeradores se obtienen normalmente llamando al GetEnumerator()método de un objeto que implementa la IEnumerableinterfaz. [ 18 ] : 54–56 [ 19 ] : 54–56 una Currentpropiedad, para obtener el valor del elemento al que apunta actualmente; [ 17 ] : 344 [ 18 ] : 56 [ 19 ] : 89 Las clases contenedoras normalmente implementan esta interfaz. Sin embargo, la instrucción foreach en C# puede operar en cualquier objeto que proporcione dicho método, incluso si no lo implementa IEnumerable( tipado dinámico ). [ 19 ] : 89 Ambas interfaces se ampliaron a versiones genéricas en .NET 2.0 .
A continuación se muestra un uso sencillo de iteradores en C# 2.0:
using System ; using System.Collections.Generic ;// versión explícita IEnumerator < MyType > iter = list . GetEnumerator (); while ( iter . MoveNext ()) { Console . WriteLine ( iter . Current ); }// versión implícita foreach ( MyType value in list ) { Console . WriteLine ( value ); }C# 2.0 también admite generadores : un método que se declara como que devuelve IEnumerator(o IEnumerable), pero usa la yield returninstrucción " " para producir una secuencia de elementos en lugar de devolver una instancia de objeto, será transformado por el compilador en una nueva clase que implementa la interfaz apropiada.
do
Los iteradores no existen de forma nativa en C , pero pueden emularse de alguna manera con aritmética de punteros .
A continuación se muestra una lista enlazada intcon iterador.
typedef struct LinkedList { int * valor ; struct LinkedList * siguiente ; } LinkedList ;typedef struct { LinkedList * current ; } LinkedListIterator ;LinkedListIterator list_begin ( LinkedList * head ) { LinkedListIterator it = { head }; return it ; }int * list_next ( LinkedListIterator * it ) { if ( ! it -> current ) { return NULL ; } int * value = & it -> current -> value ; it -> current = it -> current -> next ; return value ; }int main () { // ... for ( LinkedListIterator it = list_begin ( head ); ; ) { int * val = list_next ( & it ); if ( ! val ) { break ; } printf ( "%d \n " , * val ); } }C++
El lenguaje C++ utiliza ampliamente iteradores en su biblioteca estándar y describe varias categorías de iteradores que difieren en el repertorio de operaciones que permiten. Estas incluyen iteradores hacia adelante , iteradores bidireccionales e iteradores de acceso aleatorio , en orden creciente de posibilidades. Todos los tipos de plantillas de contenedores estándar proporcionan iteradores de una de estas categorías. Los iteradores generalizan los punteros a elementos de un array (que de hecho pueden usarse como iteradores), y su sintaxis está diseñada para asemejarse a la aritmética de punteros de C , donde los operadores `and` se usan para referenciar el elemento al que apunta el iterador y los operadores de aritmética de punteros como `and` se usan para modificar iteradores en el recorrido de un contenedor.*->++
El recorrido mediante iteradores generalmente implica un iterador variable y dos iteradores fijos que delimitan el rango a recorrer. La distancia entre los iteradores, en términos del número de aplicaciones del operador ++necesarias para transformar el límite inferior en el superior, es igual al número de elementos en el rango designado; el número de valores de iterador distintos involucrados es uno más. Por convención, el iterador límite inferior apunta al primer elemento del rango, mientras que el iterador límite superior no apunta a ningún elemento del rango, sino justo más allá del final del mismo. Para recorrer un contenedor completo, el begin()método proporciona el límite inferior y end()el límite superior. Este último no hace referencia a ningún elemento del contenedor, sino que es un valor de iterador válido con el que se puede comparar.
El siguiente ejemplo muestra un uso típico de un iterador.
importar std ;usando std :: vector ;int main () { vector < int > items ; items . push_back ( 5 ); // Agrega el valor entero '5' al vector 'items'. items . push_back ( 2 ); // Agrega el valor entero '2' al vector 'items'. items . push_back ( 9 ); // Agrega el valor entero '9' al vector 'items'.// Iterar a través de 'items'. // 'it' es de tipo vector<int>::iterator for ( auto it = items . begin (), end = items . end (); it != end ; ++ it ) { // E imprimir el valor de 'items' para el índice actual. std :: print ( "{}" , * it ); }// Usando bucles foreach (añadidos en C++11), // se puede hacer lo mismo sin usar ningún iterador explícito: for ( int x : items ) { // Imprime el valor de cada elemento 'x' de 'items'. std :: print ( "{}" , x ); }// Ambos bucles for imprimen "529". }Los tipos de iteradores son independientes de los tipos de contenedores con los que se utilizan, aunque ambos se suelen usar conjuntamente. La categoría del iterador (y, por lo tanto, las operaciones definidas para él) suele depender del tipo de contenedor; por ejemplo, los arrays o vectores proporcionan iteradores de acceso aleatorio, mientras que los conjuntos (que utilizan una estructura enlazada como implementación) solo proporcionan iteradores bidireccionales. Un mismo tipo de contenedor puede tener más de un tipo de iterador asociado; por ejemplo, el std::vector<T>tipo de contenedor permite el recorrido utilizando punteros (sin procesar) a sus elementos (de tipo T*), o valores de un tipo especial std::vector<T>::iterator, y se proporciona otro tipo para los "iteradores inversos", cuyas operaciones se definen de tal manera que un algoritmo que realiza un recorrido habitual (hacia adelante) en realidad realizará el recorrido en orden inverso cuando se le llame con iteradores inversos. La mayoría de los contenedores también proporcionan un const_iteratortipo separado, para el cual no se definen intencionadamente operaciones que permitan cambiar los valores a los que apuntan.
El recorrido simple de un objeto contenedor o un rango de sus elementos (incluida la modificación de esos elementos a menos que const_iteratorse use un iterador) se puede hacer usando solo iteradores. Pero los tipos de contenedor también pueden proporcionar métodos como inserto eraseque modifican la estructura del contenedor en sí; estos son métodos de la clase contenedor, pero además requieren uno o más valores de iterador para especificar la operación deseada. Si bien es posible tener varios iteradores apuntando al mismo contenedor simultáneamente, las operaciones que modifican la estructura pueden invalidar ciertos valores de iterador (el estándar especifica para cada caso si esto puede ser así); usar un iterador invalidado es un error que dará lugar a un comportamiento indefinido , y tales errores no necesitan ser señalados por el sistema de tiempo de ejecución.
La iteración implícita también es parcialmente compatible con C++ mediante el uso de plantillas de funciones estándar, como std::for_each()y std::copy() . std::accumulate()
Cuando se utilizan, deben inicializarse con iteradores existentes, normalmente beginy end, que definen el rango sobre el que se produce la iteración. Pero posteriormente no se expone ningún objeto iterador explícito a medida que avanza la iteración. Este ejemplo muestra el uso de for_each.
// Cualquier tipo de contenedor estándar de elementos ItemType. ContainerType < ItemType > c ;// Función que procesará cada elemento de la colección. void processItem ( const ItemType & i ) { std :: println ( "{}" , i ); }// Un bucle de iteración for-each. std :: for_each ( c.begin ( ), c.end (), processItem ) ; // usando std::ranges :: for_each ( c , processItem ) ;Lo mismo se puede lograr usando std::copy, pasando un std::ostream_iteratorvalor como tercer iterador:
using std :: cout ; using std :: ostream_iterator ;std :: copy ( c.begin ( ) , c.end ( ), ostream_iterator <ItemType> ( cout , " \n " ) ) ; // usando std::ranges: std :: ranges :: copy ( c , ostream_iterator <ItemType> ( cout , " \ n " ) );Desde C++11 , la sintaxis de las funciones lambda permite especificar la operación que se iterará en línea, evitando la necesidad de definir una función con nombre. Aquí hay un ejemplo de iteración for-each usando una función lambda:
ContainerType < ItemType > c ; // Cualquier tipo de contenedor estándar de elementos ItemType.// Un bucle de iteración for-each con una función lambda. std :: for_each ( c.begin ( ), c.end (), []( const ItemType & i ) - > void { std :: println ( "{}" , i ); });// Usando std::ranges: std :: ranges :: for_each ( c , []( const ItemType & i ) -> void { std :: println ( "{}" , i ); });La sintaxis al estilo Java para iteradores ( Iterator<T>, vs T::iterator) se puede implementar en C++ de la siguiente manera:
importar std ;espacio de nombres { // concepto auxiliar: si tiene iterador plantilla < typename T > concepto Has_iterator = requiere { typename T :: iterador }; }template < typename Iterable > requiere Has_iterator < Iterable > usando Iterator = typename Iterable :: iterator ;// Ahora se puede llamar así:plantilla < typename T > usando Vector = std :: vector < T > ;int main () { Vector <int> v { 1 , 2 , 3 , 4 , 5 } ; for ( Iterator < Vector <int> > it = v.begin ( ); it != v.end ( ) ; ++ it ) { std :: println ( " { }" , * it ) ; } }Java
Introducida en la versión 1.2 de Java JDK, la java.util.Iteratorinterfaz permite la iteración de clases contenedoras. Cada una Iteratorproporciona un método next()y , [ 20 ] : 294–295 y opcionalmente puede admitir un método [ 20 ] : 262, 266. Los iteradores son creados por la clase contenedora correspondiente, normalmente por un método llamado . [ 21 ] [ 20 ] : 99 [ 20 ] : 217hasNext()remove()iterator()
El next()método avanza el iterador y devuelve el valor al que apunta el iterador. El primer elemento se obtiene en la primera llamada a next(). [ 20 ] : 294–295 Para determinar cuándo se han visitado todos los elementos del contenedor hasNext()se utiliza el método de prueba. [ 20 ] : 262 El siguiente ejemplo muestra un uso sencillo de iteradores:
import java.util.Iterator ;Iterador iter = lista.iterador ( ) ; Iterador <MyType> iter = lista.iterador ( ) ; // Iterador iter = lista.iterador ( ) ; antes de J2SE 5.0 while ( iter.hasNext ( ) ) { System.out.print ( iter.next ( ) ) ; if ( iter.hasNext ( ) ) { System.out.print ( " , " ) ; } }Para demostrar que hasNext()se puede llamar repetidamente, lo usamos para insertar comas entre los elementos, pero no después del último elemento.
Este enfoque no separa adecuadamente la operación de avance del acceso real a los datos. Si el elemento de datos debe usarse más de una vez para cada avance, debe almacenarse en una variable temporal . Cuando se requiere un avance sin acceso a los datos (es decir, para omitir un elemento de datos determinado), el acceso se realiza igualmente, aunque el valor devuelto se ignora en este caso.
Para los tipos de colección que lo admiten, el remove()método del iterador elimina el elemento visitado más recientemente del contenedor, manteniendo el iterador utilizable. Agregar o eliminar elementos llamando a los métodos del contenedor (también desde el mismo hilo ) hace que el iterador sea inutilizable. Intentar obtener el siguiente elemento genera una excepción. También se genera una excepción si no quedan más elementos ( hasNext()si previamente ha devuelto falso).
Además, java.util.Listexiste java.util.ListIteratoruna API similar pero que permite la iteración hacia adelante y hacia atrás, proporciona su índice actual en la lista y permite establecer el elemento de la lista en su posición.
La versión J2SE 5.0 de Java introdujo la Iterableinterfaz para admitir un bucle for( foreach ) mejorado para iterar sobre colecciones y matrices. Iterabledefine el iterator()método que devuelve un Iterator. [ 20 ] : 266 Usando el forbucle mejorado, el ejemplo anterior se puede reescribir como
for ( MyType obj : list ) { System . out . print ( obj ); }Algunos contenedores también utilizan la Enumerationclase anterior (desde la versión 1.0). Proporciona hasMoreElements()métodos nextElement(), pero no tiene métodos para modificar el contenedor.
Scala
En Scala , los iteradores cuentan con un amplio conjunto de métodos similares a los de las colecciones, y pueden utilizarse directamente en bucles `for`. De hecho, tanto los iteradores como las colecciones heredan de un rasgo base común: `<iterador>` scala.collection.TraversableOnce. Sin embargo, debido al amplio conjunto de métodos disponibles en la biblioteca de colecciones de Scala, como map` <iterador>`, `<recopilador>`, `<recopilador>` collect, filteretc., a menudo no es necesario trabajar directamente con iteradores al programar en Scala.
Los iteradores y colecciones de Java se pueden convertir automáticamente en iteradores y colecciones de Scala, respectivamente, simplemente agregando una sola línea.
import scala . collection . JavaConversions . _al archivo. El JavaConversionsobjeto proporciona conversiones implícitas para ello. Las conversiones implícitas son una característica de Scala: métodos que, cuando son visibles en el ámbito actual, insertan automáticamente llamadas a sí mismos en las expresiones relevantes en el lugar apropiado para que pasen la verificación de tipos cuando de otro modo no la pasarían.
MATLAB
MATLAB admite iteración implícita tanto externa como interna utilizando matrices "nativas" o cellmatrices. En el caso de la iteración externa, donde el usuario es responsable de avanzar el recorrido y solicitar los siguientes elementos, se puede definir un conjunto de elementos dentro de una estructura de almacenamiento de matriz y recorrer los elementos utilizando la forconstrucción de bucle. Por ejemplo,
% Definir una matriz de enteros myArray = [ 1 , 3 , 5 , 7 , 11 , 13 ];para n = myArray % ... hacer algo con n disp ( n ) % Mostrar el número entero en la ventana de comandos finRecorre una matriz de enteros usando la forpalabra clave.
En el caso de la iteración interna, donde el usuario puede proporcionar una operación al iterador para que la realice sobre cada elemento de una colección, muchos operadores integrados y funciones de MATLAB se sobrecargan para ejecutarse sobre cada elemento de una matriz y devolver implícitamente una matriz de salida correspondiente. Además, las funciones arrayfuny cellfunse pueden aprovechar para realizar operaciones personalizadas o definidas por el usuario sobre matrices "nativas" y cellmatrices respectivamente. Por ejemplo,
función simpleFun % Define una matriz de enteros myArray = [ 1 , 3 , 5 , 7 , 11 , 13 ];% Realiza una operación personalizada sobre cada elemento myNewArray = arrayfun (@( a ) myCustomFun ( a ), myArray );% Muestra el array resultante en la ventana de comandos myNewArrayfunción outScalar = myCustomFun ( inScalar ) % Simplemente multiplica por 2 outScalar = 2 * inScalar ;define una función principal simpleFunque aplica implícitamente una subfunción personalizada myCustomFuna cada elemento de una matriz utilizando una función integrada arrayfun.
Alternativamente, puede ser deseable abstraer los mecanismos del contenedor de almacenamiento de matrices del usuario definiendo una implementación personalizada orientada a objetos de MATLAB del Patrón Iterador. Dicha implementación que admite iteración externa se muestra en el elemento de MATLAB Central File Exchange Patrón de diseño: Iterador (comportamiento) . Esto está escrito en la nueva sintaxis de definición de clase introducida con la versión 7.6 (R2008a) del software MATLAB y presenta una realización de matriz unidimensional celldel Tipo de Datos Abstracto (TDA) Lista como mecanismo para almacenar un conjunto heterogéneo (en tipo de datos) de elementos. Proporciona la funcionalidad para el recorrido explícito hacia adelante de la Lista con los hasNext()métodos next()y reset()para usar en un whilebucle.
PHP

foreachEl bucle de PHP se introdujo en la versión 4.0 y se hizo compatible con objetos como valores en la versión 4.0 Beta 4. [ 22 ] Sin embargo, el soporte para iteradores se agregó en PHP 5 mediante la introducción de la interfaz interna [ 23 ]Traversable . [ 24 ] Las dos interfaces principales para la implementación en scripts PHP que permiten iterar objetos a través del foreachbucle son Iteratory IteratorAggregate. Esta última no requiere que la clase implementadora declare todos los métodos necesarios, sino que implementa un método de accesogetIterator ( ) que devuelve una instancia de Traversable. La biblioteca estándar de PHP proporciona varias clases para trabajar con iteradores especiales. [ 25 ] PHP también admite generadores desde la versión 5.5. [ 26 ]
La implementación más sencilla consiste en envolver un array, lo que puede resultar útil para sugerir tipos y ocultar información .
espacio de nombres Wikipedia\Iterador ;clase final ArrayIterator extiende \Iterator { matriz privada $array ;función pública __construct ( array $array ) { $this -> array = $array ; }public function rewind () : void { echo 'rebobinando' , PHP_EOL ; reset ( $this -> array ); }public function current () { $value = current ( $this -> array ); echo "current: { $value } " , PHP_EOL ; return $value ; }public function key () { $key = key ( $this -> array ); echo "clave: { $key } " , PHP_EOL ; return $key ; }public function next () { $value = next ( $this -> array ); echo "siguiente: { $value } " , PHP_EOL ; return $value ; }public function valid () : bool { $valid = $this -> current () !== false ; echo 'válido: ' , ( $valid ? 'true' : 'false' ), PHP_EOL ; return $valid ; } }Todos los métodos de la clase de ejemplo se utilizan durante la ejecución de un bucle foreach completo ( foreach ($iterator as $key => $current) {}). Los métodos del iterador se ejecutan en el siguiente orden:
$iterator->rewind()Garantiza que la estructura interna comience desde el principio.$iterator->valid()En este ejemplo, devuelve verdadero .$iterator->current()El valor devuelto se almacena en$value.$iterator->key()El valor devuelto se almacena en$key.$iterator->next()avanza al siguiente elemento en la estructura interna.$iterator->valid()Devuelve falso y el bucle se interrumpe.
El siguiente ejemplo ilustra una clase PHP que implementa la Traversableinterfaz, la cual podría encapsularse en una IteratorIteratorclase para procesar los datos antes de que regresen al foreachbucle. Su uso, junto con la MYSQLI_USE_RESULTconstante, permite que los scripts PHP iteren conjuntos de resultados con miles de millones de filas con un consumo mínimo de memoria. Estas características no son exclusivas de PHP ni de sus implementaciones de clases MySQL (por ejemplo, la clase también PDOStatementimplementa la interfaz).Traversable
mysqli_report ( MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT ); $mysqli = new \mysqli ( 'host.example.com' , 'username' , 'password' , 'database_name' );// La clase \mysqli_result que devuelve la llamada al método implementa la interfaz interna Traversable. foreach ( $mysqli -> query ( 'SELECT `a`, `b`, `c` FROM `table`' , MYSQLI_USE_RESULT ) as $row ) { // Actuar sobre la fila devuelta, que es un array asociativo. }Pitón
Los iteradores en Python son una parte fundamental del lenguaje y, en muchos casos, pasan desapercibidos, ya que se utilizan implícitamente en la instrucción for` foreach` , en las comprensiones de listas y en las expresiones generadoras . Todos los tipos de colecciones estándar integrados de Python admiten iteración, al igual que muchas clases que forman parte de la biblioteca estándar. El siguiente ejemplo muestra una iteración implícita típica sobre una secuencia:
para cada valor en la secuencia : imprimir ( valor )Los diccionarios de Python (una forma de matriz asociativa ) también se pueden iterar directamente, cuando se devuelven las claves del diccionario; o items()se puede iterar sobre el método de un diccionario, donde produce pares clave-valor correspondientes como una tupla:
para cada clave en diccionario : valor = diccionario [ clave ] imprimir ( clave , valor )para clave , valor en diccionario.items ( ) : imprimir ( clave , valor )Sin embargo, los iteradores pueden usarse y definirse explícitamente. Para cualquier tipo o clase de secuencia iterable, iter()se utiliza la función integrada para crear un objeto iterador. Este objeto iterador puede recorrerse con la next()función, que internamente utiliza el __next__()método, el cual devuelve el siguiente elemento del contenedor. (La afirmación anterior se aplica a Python 3.x. En Python 2.x, el método es equivalente). Se generará next()una excepción cuando no queden más elementos. El siguiente ejemplo muestra una iteración equivalente sobre una secuencia utilizando iteradores explícitos:StopIteration
from typing import Iteratorsecuencia : lista [ int ] = [ 1 , 2 , 3 , 4 ] it : iterador [ int ] = iter ( secuencia ) mientras True : intentar : valor = it.siguiente () # en Python 2.x valor = siguiente ( it ) # en Python 3.x excepto StopIteration : romper imprimir ( valor )Cualquier clase definida por el usuario puede admitir la iteración estándar (implícita o explícita) definiendo un __iter__()método que devuelva un objeto iterador. Este objeto iterador, a su vez, debe definir un __next__()método que devuelva el siguiente elemento.
Los generadores de Python implementan este protocolo de iteración .
Raku
Los iteradores en Raku son una parte fundamental del lenguaje, aunque normalmente los usuarios no tienen que preocuparse por ellos. Su uso está oculto tras las API de iteración, como la forinstrucción, map, grep, la indexación de listas con .[$idx], etc.
El siguiente ejemplo muestra una iteración implícita típica sobre una colección de valores:
mis @valores = 1 , 2 , 3 ; para @valores -> $valor { decir $valor } # SALIDA: # 1 # 2 # 3Los hashes de Raku también se pueden iterar directamente, lo que produce Pairobjetos clave-valor. kvSe puede invocar un método sobre el hash para iterar sobre la clave y los valores; otro keysmétodo para iterar sobre las claves del hash; y otro valuesmétodo para iterar sobre los valores del hash.
mi %palabra-a-número = 'uno' => 1 , 'dos' => 2 , 'tres' => 3 ; para %palabra-a-número -> $par { decir $par ; } # SALIDA: # tres => 3 # uno => 1 # dos => 2para %word-to-number . kv -> $key , $value { decir "$key: $value" } # SALIDA: # tres: 3 # uno: 1 # dos: 2para %word-to-number . keys -> $key { decir "$key => " ~ %word-to-number { $key }; } # SALIDA: # tres => 3 # uno => 1 # dos => 2Sin embargo, los iteradores pueden usarse y definirse explícitamente. Para cualquier tipo iterable, existen varios métodos que controlan diferentes aspectos del proceso de iteración. Por ejemplo, iteratorse supone que un método debe devolver un Iteratorobjeto, y otro pull-onemétodo debe generar y devolver el siguiente valor si es posible, o devolver el valor centinelaIterationEnd si no se pueden generar más valores. El siguiente ejemplo muestra una iteración equivalente sobre una colección utilizando iteradores explícitos:
mis @valores = 1 , 2 , 3 ; mi $it := @valores.iterador ; # obtener iterador para @valoresbucle { mi $valor := $it . pull-one ; # obtener el siguiente valor de la iteración last if $valor =:= IterationEnd ; # detenerse si llegamos al final de la iteración say $valor ; } # SALIDA: # 1 # 2 # 3En Raku, todos los tipos iterables componen el Iterablerol, Iteratorel rol o ambos. El Iterablees bastante simple y solo requiere que iteratorla clase que lo compone implemente. El Iteratores más complejo y proporciona una serie de métodos como pull-one, que permiten una operación de iteración más precisa en varios contextos, como agregar o eliminar elementos, o saltarlos para acceder a otros elementos. Por lo tanto, cualquier clase definida por el usuario puede admitir la iteración estándar componiendo estos roles e implementando los métodos iteratory/o .pull-one
La DNAclase representa una cadena de ADN y la implementa iteratorcomponiendo el Iterablerol. La cadena de ADN se divide en un grupo de trinucleótidos cuando se itera sobre ella:
subconjunto Strand de Str donde { . coincidencia ( /^^ <[ACGT]>+ $$/ ) y . caracteres %% 3 }; clase DNA hace Iterable { tiene $.chain ; método nuevo ( Strand:D $chain ) { self . bendice: : $chain } método iterador ( ADN:D: ){ $.cadena . peine . rotor ( 3 ). iterador } }; para el ADN . nuevo ( 'GATTACATA' ) { . decir } # SALIDA: # (GAT) # (TAC) # (ATA)diga ADN . nuevo ( 'GATTACATA' ). map (*. join ). join ( '-' ); # SALIDA: # GAT-TAC-ATALa Repeaterclase se compone de los roles Iterabley :Iterator
clase Repeater hace Iterable hace Iterator { tiene Any $.item es requerido ; tiene Int $.times es requerido ; tiene Int $!count = 1 ; multi método nuevo ( $item , $times ) { self . bless: : $item , : $times ; } método iterador { self } método pull-one (--> Mu ){ si $!count <= $!times { $!count += 1 ; return $!item } de lo contrario { devolver Fin de iteración } } } para Repeater.new ( "Hola " , 3 ) { . decir } # SALIDA: # Hola # Hola # HolaRubí
Ruby implementa los iteradores de forma bastante diferente; todas las iteraciones se realizan pasando funciones de devolución de llamada a métodos contenedores. De esta manera, Ruby no solo implementa la iteración básica, sino también varios patrones de iteración como el mapeo de funciones, los filtros y la reducción. Ruby también admite una sintaxis alternativa para el método de iteración básico each; los siguientes tres ejemplos son equivalentes:
( 0 ... 42 ) . cada uno hace | n | pone n fin...y...
para n en 0 ... 42 pone n fino incluso más corto
42 . veces hacer | n | pone n finRuby también puede iterar sobre listas fijas usando Enumerators y llamando a su #nextmétodo o haciendo un for each sobre ellas, como se indicó anteriormente.
Óxido
Rust utiliza iteradores externos en toda la biblioteca estándar, incluso en su forbucle, que llama implícitamente al next()método de un iterador hasta que se consume. El forbucle más básico, por ejemplo, itera sobre un Rangetipo:
for i in 0 .. 42 { println! ( "{}" , i ); } // Imprime los números del 0 al 41Específicamente, el forbucle llamará al into_iter()método de un valor, que devuelve un iterador que a su vez proporciona los elementos al bucle. El forbucle (o cualquier método que consuma el iterador) continúa hasta que el next()método devuelve un Nonevalor (las iteraciones que producen elementos devuelven un Some(T)valor, donde Tes el tipo de elemento).
Todas las colecciones proporcionadas por la biblioteca estándar implementan el IntoIteratorrasgo (es decir, definen el into_iter()método). Los iteradores también implementan el Iteratorrasgo, lo que requiere definir el next()método. Además, cualquier tipo que implemente Iteratorrecibe automáticamente una implementación que IntoIteratordevuelve el mismo método.
Los iteradores admiten varios adaptadores ( map(), filter(), skip(), take(), etc.) como métodos proporcionados automáticamente por el Iteratorrasgo.
Los usuarios pueden crear iteradores personalizados creando un tipo que implemente la Iteratorcaracterística. Las colecciones personalizadas pueden implementar la IntoIteratorcaracterística y devolver un tipo de iterador asociado para sus elementos, lo que permite su uso directo en forbucles. A continuación, el Fibonaccitipo implementa un iterador personalizado sin límite:
struct Fibonacci ( u64 , u64 );impl Fibonacci { pub fn new () -> Self { Self ( 0 , 1 ) } }iterador de implementación para Fibonacci { tipo Item = u64 ;fn next ( & mut self ) -> Option < Self :: Item > { let next = self . 0 ; self . 0 = self . 1 ; self . 1 = self . 0 + next ;Algunos ( siguiente ) } }fn main () { let fib = Fibonacci :: new (); for n in fib . skip ( 1 ). step_by ( 2 ). take ( 4 ) { println! ( "{n}" ); } // Imprime 1, 2, 5 y 13 }![]()
Véase también
- Iterar
- Patrón de diseño (informática) : plantilla de solución reutilizable para un comportamiento de software de uso común. Páginas que muestran breves descripciones de los destinos de redirección.
- Generador (programación informática) – Rutina que genera una secuencia de valores
- Alcance (informática)
- Patrón Visitor – Patrón de diseño de software
Referencias
- ↑ Gatcomb, Joshua (16 de junio de 2005). "Understanding and Using Iterators" . Perl.com . Consultado el 8 de agosto de 2012. Un
iterador definido por el usuario suele tener la forma de una referencia de código que, al ejecutarse, calcula el siguiente elemento de una lista y lo devuelve. Cuando el iterador llega al final de la lista, devuelve un valor acordado.
- 1 2 Watt, Stephen M. (16 de septiembre de 2006). "Una técnica para la iteración genérica y su optimización" (PDF) . Universidad de Western Ontario, Departamento de Ciencias de la Computación . Recuperado el 8 de agosto de 2012.
Los iteradores se introdujeron como construcciones para permitir bucles sobre estructuras de datos abstractas sin revelar su representación interna.
- ↑ Alex Allain. "Iteradores STL" . Cprogramming.com - Tu recurso para C y C++ . Consultado el 8 de agosto de 2012. Puedes
pensar en un iterador como un puntero a un elemento que forma parte de un contenedor más grande de elementos.
- ↑ Liskov, B .; Snyder, A.; Atkinson, R.; Schaffert, C. (agosto de 1977). "Mecanismos de abstracción en CLU". Communications of the ACM . 20 (8): 564– 576. CiteSeerX 10.1.1.112.656 . doi : 10.1145/359763.359789 . S2CID 17343380 .
- 1 2 "Diferencia entre un iterador externo y un iterador interno" . CareerRide.COM. 3 de abril de 2009. Archivado del original el 19 de septiembre de 2012. Consultado el 8 de agosto de 2012. Un
iterador interno se implementa mediante las funciones miembro de la clase que contiene la lógica de iteración. Un iterador externo se implementa mediante una clase independiente que se puede adjuntar al objeto que contiene la lógica de iteración. La ventaja del iterador externo es que se pueden activar varios iteradores simultáneamente en el mismo objeto.
{{cite web}}: CS1 maint: bot: estado de la URL original desconocido ( enlace ) - ↑ Watt, Stephen M. « Una técnica para la iteración genérica y su optimización» . Universidad de Western Ontario, Departamento de Ciencias de la Computación. Archivado del original el 6 de agosto de 2012. Consultado el 8 de agosto de 2012.
Algunos autores utilizan el término iterador, y otros el término generador. Algunos establecen distinciones sutiles entre ambos.
{{cite web}}: CS1 maint: bot: estado de la URL original desconocido ( enlace ) - 1 2 Freeman, Eric; Freeman, Elisabeth; Kathy, Sierra; Bert, Bates (2004). Hendrickson, Mike; Loukides, Mike (eds.). Head First Design Patterns (edición de bolsillo) . Vol. 1. O'REILLY. pág. 338. ISBN 978-0-596-00712-6. Consultado el 9 de agosto de 2012 .
- ↑ "Glosario — Documentación de Python 3.8.4" . Consultado el 15 de julio de 2020 .
- ↑ Vecerina, Ivan (1 de febrero de 2006). "Índice vs. iterador" . BYTES. Archivado del original el 9 de agosto de 2012. Consultado el 8 de agosto de 2012.
Un índice solo se puede usar para contenedores que admiten (eficientemente) acceso aleatorio (es decir, acceso directo a un elemento en una posición dada). Un iterador es un concepto más general. Los iteradores ofrecen un recorrido eficiente de listas enlazadas, archivos y varias otras estructuras de datos. A menudo conduce a la generación de código más eficiente.
{{cite web}}: CS1 maint: bot: estado de la URL original desconocido ( enlace ) - ↑ Kevin Waterson. "C++ Iteratoren: Iterator-Kategorien" (en alemán). cppreference.com . Consultado el 9 de agosto de 2012 .
- ↑ Kevin Waterson. "Iteradores: conceptos" . sgi . Consultado el 9 de agosto de 2012 .
- ↑ "DoubleEndedIterator en std::iter - Rust" . doc.rust-lang.org . Consultado el 11 de mayo de 2025 .
- ↑ larsmans (06-03-2011). "Tipos de iteradores: iteradores de salida, entrada, avance y acceso aleatorio" . stackoverflow. Archivado del original el 08-08-2012 . Consultado el 09-08-2012 .
{{cite web}}: CS1 maint: bot: estado de la URL original desconocido ( enlace ) - ↑ Kevin Waterson. "Introducción a SPL: Introducción a la biblioteca estándar de PHP (SPL)" . PHPRO.ORG . Consultado el 9 de agosto de 2012 .
- ↑ Collier, Andrew. "Iterators in R" . Archivado del original el 18 de octubre de 2018. Recuperado el 16 de noviembre de 2013 .
- ↑ "concurrent_unordered_set Template Class" . Intel Threading Building Blocks for Open Source. Archivado del original el 1 de mayo de 2015. Consultado el 9 de agosto de 2012.
•Los tipos de iterador iterator y const_iterator pertenecen a la categoría de iteradores hacia adelante
. - ^ Albahari , José (2022 ) . C# 10 en pocas palabras . O'Reilly. ISBN 978-1-098-12195-2.
- 1 2 3 4 5 Skeet, Jon (23 de marzo de 2019). C# en profundidad . Manning. ISBN 978-1617294532.
- 1 2 3 4 5 Price, Mark J. C# 8.0 y .NET Core 3.0: Desarrollo multiplataforma moderno: Cree aplicaciones con C#, .NET Core, Entity Framework Core, ASP.NET Core y ML.NET usando Visual Studio Code . Packt. ISBN 978-1-098-12195-2.
- 1 2 3 4 5 6 7 Bloch, Joshua (2018). "Java eficaz: Guía del lenguaje de programación" (tercera ed.). Addison-Wesley. ISBN 978-0134685991.
- ↑ "java.util: Interface Iterator<E>: Method Summary" . Oracle . Consultado el 8 de agosto de 2012 .
- ↑ "Registro de cambios de PHP 4" . The PHP Group. 2000-02-20 . Consultado el 2015-10-13 .
- ↑ Interno se refiere al hecho de que la interfaz no se puede implementar en scripts PHP, solo en el código fuente C (lenguaje de programación) .
- ↑ "La interfaz Traversable" . The PHP Group . Consultado el 13 de octubre de 2015 .
- ↑ "Iteradores" . The PHP Group . Consultado el 13 de octubre de 2015 .
- ↑ "Registro de cambios de PHP 5" . The PHP Group. 2013-06-20 . Consultado el 2015-10-13 .
Enlaces externos
- Explicación de Iterator, Iterable y ListIterator en Java
- Interfaz .NET
- Artículo " Comprender y utilizar los iteradores " de Joshua Gatcomb
- Artículo " Una técnica para la iteración genérica y su optimización" archivado el 9 de julio de 2020 en Wayback Machine (217 KB) por Stephen M. Watt
- Iteradores
- Biblioteca iteradora Boost C++
- Interfaz Java
- PHP: Iteración de objetos
- Iteradores STL
- ¿Qué son los iteradores? - Descripción de referencia
- Iteración en programación
- Objeto (informática)
- Tipos de datos abstractos