stringtranslate.com

Iterador

En programación informática , un iterador es un objeto que proporciona acceso progresivamente a cada elemento de una colección , en orden. [1] [2] [3]

Una colección puede proporcionar múltiples iteradores a través de su interfaz que proporcionan 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, a menudo, está estrechamente acoplado a la colección para permitir la semántica operativa del iterador.

Un iterador tiene un comportamiento similar al cursor de una base de datos .

Los iteradores datan del lenguaje de programación CLU en 1974.

Patrón

Un iterador proporciona acceso a un elemento de una colección ( acceso al elemento ) y puede cambiar su estado interno para proporcionar acceso al siguiente elemento ( recorrido del elemento ). [4] También prevé la creación e inicialización de un primer elemento e indica si se han atravesado todos los elementos. En algunos contextos de programación, un iterador proporciona funcionalidad adicional.

Un iterador permite al consumidor procesar cada elemento de una colección mientras lo aísla de la estructura interna de la colección. [2] La colección puede almacenar elementos de cualquier forma mientras el consumidor puede acceder a ellos como una secuencia.

En la programación orientada a objetos, una clase iteradora generalmente se diseña 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 transversal y no la funcionalidad de acceso al elemento.

Generador

Una forma de implementar un iterador es mediante una forma restringida de rutina , conocida como generador . A diferencia de una subrutina , una corrutina generadora puede proporcionar valores a su interlocutor 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 los generadores preservan su estado local entre invocaciones, son particularmente adecuados para iteradores complicados y con estado, como los atravesadores de árboles . Existen diferencias y distinciones sutiles en el uso de los términos "generador" e "iterador", que varían entre autores e idiomas. [5] 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 la declaración de Python yield:

def  fibonacci ( límite ):  a ,  b  =  0 ,  1  para  _  en  el rango ( límite ):  rendimiento  a  a ,  b  =  b ,  a  +  bpara  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 toma funciones anónimas ) que atraviesa una colección mientras aplica una función a cada elemento. Por ejemplo, la función de Python mapaplica una función definida por la persona que llama a cada elemento:

dígitos  =  [ 0 ,  1 ,  2 ,  3 ,  4 ,  5 ,  6 ,  7 ,  8 ,  9 ]squared_digits  =  map ( lambda  x :  x ** 2 ,  digits ) # 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 , 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. [4] [6]

Un iterador implícito suele manifestarse en la sintaxis del lenguaje como foreach.

En Python, un objeto de colección se puede iterar directamente:

para  valor  en  iterable :  imprimir ( valor )

En Ruby, la iteración requiere acceder a una propiedad del iterador:

iterable . cada uno hace | valor | pone valor final    

Este 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 (usando una función anónima ).

Los lenguajes que admiten listas por comprensión o construcciones similares también pueden utilizar iteradores implícitos durante la construcción de la lista de resultados, como en Python:

nombres  =  [ persona . nombre  de  la persona  en  la lista  si  es persona . masculino ]

A veces la naturaleza oculta implícita es sólo parcial. El lenguaje C++ tiene algunas plantillas de funciones para 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 iterable (pero no necesariamente indexable) potencialmente infinito. Varios lenguajes, como Perl y Python, implementan flujos como iteradores. En Python, los iteradores son objetos que representan flujos de datos. [7] Las implementaciones alternativas de stream incluyen lenguajes basados ​​en datos , como AWK y sed .

Contraste con indexación

En lugar de utilizar un iterador, muchos lenguajes permiten el uso de un operador de subíndice y un contador de bucle para acceder a cada elemento. Aunque la indexación se puede utilizar con colecciones, el uso de iteradores puede tener ventajas como: [8]

La capacidad de una colección de modificarse mientras se itera a través de sus elementos se ha vuelto necesaria en la programación moderna orientada a objetos , donde las interrelaciones entre los objetos y los efectos de las operaciones pueden no ser obvias. Al utilizar un iterador, uno se aísla de este tipo de consecuencias. Sin embargo, esta afirmación debe tomarse con cautela, porque la mayoría de las veces, por razones de eficiencia, la implementación del iterador está tan estrechamente 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 moverse alrededor de sus datos en la memoria, la única forma de no invalidar el iterador es, para la colección, realizar de alguna manera un seguimiento de todos los iteradores activos actualmente y actualizarlos 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á 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 en relación con el tamaño de la colección sería utilizar una especie de mecanismo de control, es decir, una colección de punteros indirectos a los elementos de la colección que deben actualizarse con la colección, y dejar que los iteradores apunten a estos identificadores en lugar de directamente a los elementos de datos. Pero este enfoque afectará negativamente el rendimiento del iterador, ya que debe realizar un doble puntero para acceder al elemento de datos real. Por lo general, esto 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 avanzado. Por tanto, es especialmente importante tener iteradores con acceso a datos muy eficiente.

Considerándolo todo, esto siempre es una compensación entre seguridad (los iteradores siempre son válidos) y eficiencia. La mayoría de las veces, la seguridad adicional no vale el precio de eficiencia que se paga por ella. Usar una colección alternativa (por ejemplo, una lista enlazada individualmente en lugar de un vector) sería una mejor opción (globalmente más eficiente) si se necesita la estabilidad de los iteradores.

Clasificación

Categorías

Los iteradores se pueden clasificar según su funcionalidad. Aquí hay una lista (no exhaustiva) de categorías de iteradores: [9] [10]

Tipos

Los diferentes lenguajes o bibliotecas utilizados con estos lenguajes definen los tipos de iteradores. Algunos de ellos son [12]

En diferentes lenguajes de programación

.NETO

Los iteradores en .NET Framework (es decir, C#) se denominan "enumeradores" y están representados por la IEnumeratorinterfaz. [15] : 189–190, 344  [16] : 53–54 IEnumerator proporciona un MoveNext()método que avanza al siguiente elemento e indica si se ha alcanzado el final de la colección; [15] : 344  [16] : 55–56  [17] : 89  una Currentpropiedad, para obtener el valor del elemento al que se apunta actualmente. [15] : 344  [16] : 56  [17] : 89  y un Reset()método opcional, [15] : 344  para rebobinar el enumerador a su posición inicial. El enumerador inicialmente apunta a un valor especial antes del primer elemento, por lo que se requiere una llamada MoveNext()para comenzar a iterar.

Los enumeradores normalmente se obtienen llamando al GetEnumerator()método de un objeto que implementa la IEnumerableinterfaz. [16] : 54–56  [17] : 54–56  una Currentpropiedad, para obtener el valor del elemento al que se apunta actualmente; [15] : 344  [16] : 56  [17] : 89  Las clases de contenedor normalmente implementan esta interfaz. Sin embargo, la declaración foreach en C# puede operar en cualquier objeto que proporcione dicho método, incluso si no lo implementa IEnumerable( tipificación pato ). [17] : 89  Ambas interfaces se ampliaron a versiones genéricas en .NET 2.0 .

A continuación se muestra un uso simple de iteradores en C# 2.0:

// versión explícita IEnumerator < MyType > iter = lista . ObtenerEnumerador (); while ( iter . MoveNext ()) Consola . WriteLine ( iter . Actual );     // versión implícita de cada uno ( valor MyType en la lista ) Consola . WriteLine ( valor );     

C# 2.0 también admite generadores: un método que se declara como de retorno IEnumerator(o IEnumerable), pero que utiliza 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 implemente la interfaz adecuada. .

C++

El lenguaje C++ hace un amplio uso de iteradores en su biblioteca estándar y describe varias categorías de iteradores que se diferencian en el repertorio de operaciones que permiten. Estos incluyen iteradores directos , iteradores bidireccionales e iteradores de acceso aleatorio , en orden de posibilidades crecientes. Todos los tipos de plantillas de contenedores estándar proporcionan iteradores de una de estas categorías. Los iteradores generalizan punteros a elementos de una matriz (que de hecho pueden usarse como iteradores), y su sintaxis está diseñada para parecerse a la de la aritmética de punteros de C , donde los operadores y se usan para hacer referencia al elemento al que apunta el iterador y los operadores aritméticos de puntero. Los me gusta se utilizan para modificar iteradores en el recorrido de un contenedor.*->++

El recorrido mediante iteradores normalmente implica un único iterador variable y dos iteradores fijos que sirven para delimitar un rango a recorrer. La distancia entre los iteradores limitantes, 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 que eso. Por convención, el iterador limitante inferior "apunta" al primer elemento del rango, mientras que el iterador limitante superior no apunta a ningún elemento del rango, sino más bien más allá del final del rango. Para atravesar 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, pero es un valor de iterador válido con el que se puede comparar.

El siguiente ejemplo muestra un uso típico de un iterador.

std :: vector <int> elementos ;elementos . empujar_hacia atrás ( 5 ); // Agrega el valor entero '5' al vector 'elementos'. elementos . empujar_hacia atrás ( 2 ); // Agrega el valor entero '2' al vector 'elementos'. elementos . empujar_back ( 9 ); // Agrega el valor entero '9' al vector 'elementos'.    for ( auto it = elementos . comenzar (); it != elementos . end (); ++ it ) { // Iterar a través de 'elementos'. std :: cout << * eso ; // E imprimir el valor de los 'elementos' para el índice actual. } // En C++11, se puede hacer lo mismo sin utilizar ningún iterador: for ( auto x : items ) { std :: cout << x ; // Imprime el valor de cada elemento 'x' de 'items'. }                       // Ambos bucles for imprimen "529".

Los tipos de iterador están separados de los tipos de contenedor con los que se usan, aunque los dos a menudo se usan juntos. La categoría del iterador (y por lo tanto las operaciones definidas para él) generalmente depende del tipo de contenedor; por ejemplo, las matrices o los vectores proporcionan iteradores de acceso aleatorio, pero los conjuntos (que utilizan una estructura vinculada 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 ya sea usando punteros (sin formato) a sus elementos (de tipo *<T>), o valores de un tipo especial std::vector<T>::iterator, y se proporciona otro tipo más para "iteradores inversos", cuyas operaciones se definen de tal manera que un El algoritmo que realiza un recorrido habitual (hacia adelante) en realidad lo hará 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 las operaciones que permitirían cambiar los valores señalados no están definidas intencionalmente.

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 a) se puede realizar usando iteradores únicamente. Pero los tipos de contenedores también pueden proporcionar métodos como inserto eraseque modifican la estructura del propio contenedor; Estos son métodos de la clase contenedora, pero además requieren uno o más valores de iterador para especificar la operación deseada. Si bien es posible tener múltiples iteradores apuntando al mismo contenedor simultáneamente, las operaciones de modificación de estructura pueden invalidar ciertos valores de iterador (el estándar especifica para cada caso si esto puede ser así); El uso de un iterador invalidado es un error que conducirá a un comportamiento indefinido y dichos errores no necesitan ser señalados por el sistema de tiempo de ejecución.

C++ también admite parcialmente la iteración implícita 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 en 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.

TipoContenedor < TipoArtículo > c ; // Cualquier tipo de contenedor estándar de elementos ItemType.  void ProcessItem ( const ItemType & i ) { // Función que procesará cada elemento de la colección. std :: cout << i << std :: endl ; }          std :: for_each ( c . comenzar (), c . finalizar (), ProcessItem ); // Un bucle para cada iteración.   

Se puede lograr lo mismo usando std::copy, pasando un std::ostream_iteratorvalor como tercer iterador:

std :: copiar ( c . comenzar (), c . terminar (), std :: ostream_iterator < ItemType > ( std :: cout , " \n " ));   

Desde C++ 11 , la sintaxis de la función lambda se puede usar para especificar que la operación se itere en línea, evitando la necesidad de definir una función con nombre. A continuación se muestra un ejemplo de iteración para cada uso de una función lambda:

TipoContenedor < TipoArtículo > c ; // Cualquier tipo de contenedor estándar de elementos ItemType.  // Un bucle de iteración para cada uno con una función lambda. std :: for_each ( c . comenzar (), c . end (), []( const ItemType & i ) { std :: cout << i << std :: endl ; });           

Java

Introducida en la versión Java JDK 1.2, la java.util.Iteratorinterfaz permite la iteración de clases de contenedor. Cada uno Iteratorproporciona un método next()y , [18] : 294–295  y, opcionalmente, puede admitir un método [18] : 262, 266  . Los iteradores son creados por la clase contenedora correspondiente, normalmente mediante un método denominado . [19] [18] : 99  [18] : 217 hasNext()remove()iterator()

El next()método hace avanzar el iterador y devuelve el valor señalado por el iterador. El primer elemento se obtiene con la primera llamada a next(). [18] : 294–295  Para determinar cuándo se han visitado todos los elementos del contenedor, hasNext()se utiliza el método de prueba. [18] : 262  El siguiente ejemplo muestra un uso simple de iteradores:

Iterador iter = lista . iterador (); // Iterador<MiTipo> iter = lista.iterador(); // en J2SE 5.0 while ( iter . hasNext ()) { System . afuera . imprimir ( iter . siguiente ()); if ( iter . hasNext ()) Sistema . afuera . imprimir ( ", " ); }         

Para mostrar 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 avanzada 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 necesita un avance sin acceso a los datos (es decir, omitir un elemento de datos determinado), el acceso se realiza de todos modos, aunque en este caso se ignora el valor devuelto.

Para los tipos de colección que lo admiten, el remove()método del iterador elimina el elemento visitado más recientemente del contenedor mientras mantiene el iterador utilizable. Agregar o eliminar elementos llamando a los métodos del contenedor (también desde el mismo hilo ) inutiliza el iterador. Un intento de obtener el siguiente elemento genera la excepción. También se lanza una excepción si no quedan más elementos ( hasNext()anteriormente devolvió falso).

Además, java.util.Listexiste una java.util.ListIteratorAPI similar pero que permite la iteración hacia adelante y hacia atrás, proporciona su índice actual en la lista y permite configurar el elemento de la lista en su posición.

La versión J2SE 5.0 de Java introdujo la Iterableinterfaz para admitir un bucle mejorado for( foreach ) para iterar sobre colecciones y matrices. Iterabledefine el iterator()método que devuelve un Iterator. [18] : 266  Usando el forbucle mejorado, el ejemplo anterior se puede reescribir como

for ( MiTipo obj : lista ) { System . afuera . imprimir ( obj ); }      

Algunos contenedores también utilizan la Enumerationclase anterior (desde 1.0). Proporciona hasMoreElements()métodos nextElement(), pero no tiene métodos para modificar el contenedor.

escala

En Scala , los iteradores tienen un rico conjunto de métodos similares a las colecciones y se pueden usar directamente en bucles for. De hecho, tanto los iteradores como las colecciones heredan de un rasgo base común: scala.collection.TraversableOnce. Sin embargo, debido al rico conjunto de métodos disponibles en la biblioteca de colecciones de Scala, como , mapetc. , a menudo no es necesario tratar directamente con iteradores cuando se programa en Scala.collectfilter

Los iteradores y colecciones de Java se pueden convertir automáticamente en iteradores y colecciones de Scala, respectivamente, simplemente agregando una sola línea.

importar escala . recopilación . Conversiones de Java . _ 

al archivo. El JavaConversionsobjeto proporciona conversiones implícitas para hacer esto. Las conversiones implícitas son una característica de Scala: métodos que, cuando son visibles en el alcance actual, insertan automáticamente llamadas a sí mismos en expresiones relevantes en el lugar apropiado para que se escriban cuando de otro modo no lo harían.

MATLAB

MATLAB admite iteraciones implícitas tanto externas como internas utilizando matrices o cellmatrices "nativas". En el caso de una iteración externa donde la responsabilidad de avanzar en el recorrido y solicitar los siguientes elementos recae en el usuario, se puede definir un conjunto de elementos dentro de una estructura de almacenamiento de matriz y recorrer los elementos utilizando la forconstrucción -loop. Por ejemplo,

% Definir una matriz de números enteros myArray = [ 1 , 3 , 5 , 7 , 11 , 13 ];  for n = myArray %... haz algo con n disp ( n ) % Echo integer hasta el final de la ventana de comandos      

atraviesa una matriz de números enteros usando la forpalabra clave.

En el caso de una iteración interna donde el usuario puede proporcionar una operación al iterador para realizarla 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 % Definir una matriz de números enteros myArray = [ 1 , 3 , 5 , 7 , 11 , 13 ];   % Realizar una operación personalizada sobre cada elemento myNewArray = arrayfun (@( a ) myCustomFun ( a ), myArray );  % Eco de la matriz 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 la función incorporada arrayfun.

Alternativamente, puede ser deseable abstraer del usuario los mecanismos del contenedor de almacenamiento de matriz definiendo una implementación personalizada de MATLAB orientada a objetos del patrón Iterador. Una implementación de este tipo que admite iteración externa se demuestra en Patrón de diseño del elemento de intercambio de archivos central de MATLAB: Iterador (comportamiento). Esto está escrito en la nueva sintaxis de definición de clase introducida con el software MATLAB versión 7.6 (R2008a) y presenta una realización de matriz unidimensional celldel tipo de datos abstractos de lista (ADT) como mecanismo para almacenar un conjunto heterogéneo (en tipo de datos) de elementos. Proporciona la funcionalidad para un recorrido de lista directo explícito con y métodos para hasNext()usar en un bucle.next()reset()while

PHP

Diagrama de clases UML de la interfaz Iterator en PHP
Diagrama de clases UML de la interfaz Iterator en PHP

foreachEl bucle de PHP se introdujo en la versión 4.0 y se hizo compatible con objetos como valores en 4.0 Beta 4. [20] Sin embargo, se agregó soporte para iteradores en PHP 5 mediante la introducción de la interfaz interna [21] . [22] Las dos interfaces principales para la implementación en scripts PHP que permiten que los objetos se iteren a través del bucle son y . Este último no requiere que la clase implementadora declare todos los métodos requeridos, sino que implementa un método de acceso ( ) que devuelve una instancia de . La biblioteca PHP estándar proporciona varias clases para trabajar con iteradores especiales. [23] PHP también admite generadores desde 5.5. [24]TraversableforeachIteratorIteratorAggregategetIteratorTraversable

La implementación más sencilla es envolver una matriz, lo que puede resultar útil para sugerencias de tipos y ocultación de información .

espacio de nombres  Wikipedia\Iterador ; clase  final ArrayIterator  extiende  \Iterator {  matriz privada  $matriz ;   función  pública __construct ( matriz  $matriz )  {  $this -> matriz  =  $matriz ;  }  función  pública rebobinar () :  void  {  echo  'rebobinado'  ,  PHP_EOL ;  restablecer ( $this -> matriz );  }  función  pública actual ()  {  $valor  =  actual ( $this -> matriz );  echo  "actual: { $valor } " ,  PHP_EOL ;  devolver  valor $ ;  }  tecla de función  pública () { $clave = clave ( $this -> matriz ); echo "clave: { $clave } " , PHP_EOL ; devolver clave $ ; }            función  pública siguiente ()  {  $valor  =  siguiente ( $this -> matriz );  echo  "siguiente: { $valor } " ,  PHP_EOL ;  devolver  valor $ ;  }  función  pública válida () :  bool  {  $válida  =  $this -> actual ()  !==  falso ;  echo  'válido: ' ,  ( $válido  ?  'verdadero'  :  'falso' ),  PHP_EOL ;  devolver  $válido ;  } }

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:

  1. $iterator->rewind()asegura que la estructura interna comienza desde el principio.
  2. $iterator->valid()devuelve verdadero en este ejemplo.
  3. $iterator->current()El valor devuelto se almacena en $value.
  4. $iterator->key()El valor devuelto se almacena en $key.
  5. $iterator->next()avanza al siguiente elemento de la estructura interna.
  6. $iterator->valid()devuelve falso y el ciclo se cancela.

El siguiente ejemplo ilustra una clase PHP que implementa la Traversableinterfaz, que podría incluirse en una IteratorIteratorclase para actuar sobre los datos antes de que se devuelvan al foreachbucle. El uso junto con la MYSQLI_USE_RESULTconstante permite que los scripts PHP iteren conjuntos de resultados con miles de millones de filas con muy poco uso 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' ,  'nombre de usuario' ,  'contraseña' ,  'nombre_base_datos' );// La clase \mysqli_result devuelta por la llamada al método implementa la interfaz interna Traversable. foreach  ( $mysqli -> query ( 'SELECT `a`, `b`, `c` FROM `table`' ,  MYSQLI_USE_RESULT )  as  $row )  {  // Actúa sobre la fila devuelta, que es una matriz asociativa. }

Pitón

Los iteradores en Python son una parte fundamental del lenguaje y en muchos casos pasan desapercibidos, ya que se usan implícitamente en la declaración for( foreach ), en listas por comprensión y en expresiones generadoras . Todos los tipos de colecciones integradas estándar de Python admiten la iteración, así como 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  valor  en  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 el items()método de un diccionario se puede iterar donde produce los pares clave-valor correspondientes como una tupla:

para  clave  en  el diccionario :  valor  =  diccionario [ clave ]  imprimir ( clave ,  valor )
para  clave ,  valor  en  el diccionario . elementos ():  imprimir ( clave ,  valor )

Sin embargo, los iteradores se pueden utilizar y definir explícitamente. Para cualquier tipo o clase de secuencia iterable, la función incorporada iter()se utiliza para crear un objeto iterador. El objeto iterador luego se puede iterar con la next()función, que utiliza el __next__()método internamente, que devuelve el siguiente elemento en el contenedor. (La declaración anterior se aplica a Python 3.x. En Python 2.x, el next()método es equivalente). StopIterationSe generará una excepción cuando no queden más elementos. El siguiente ejemplo muestra una iteración equivalente sobre una secuencia utilizando iteradores explícitos:

it  =  iter ( secuencia ) mientras que  True :  intente :  valor  =  it . next ()  # en Python 2.x  valor  =  next ( it )  # en Python 3.x  excepto  StopIteration :  break  print ( valor )

Cualquier clase definida por el usuario puede admitir la iteración estándar (ya sea implícita o explícita) definiendo un __iter__()método que devuelva un objeto iterador. Luego, el objeto iterador necesita definir un __next__()método que devuelva el siguiente elemento.

Los generadores de Python implementan este protocolo de iteración .

rakú

Los iteradores en Raku son una parte fundamental del lenguaje, aunque normalmente los usuarios no tienen que preocuparse por los iteradores. Su uso está oculto detrás de las API de iteración, como la fordeclaración map, grepla 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 { diga  $valor}# SALIDA: # 1 # 2 # 3

Los hashes de Raku también se pueden iterar directamente; esto produce Pairobjetos clave-valor. El kvmétodo se puede invocar en el hash para iterar sobre la clave y los valores; el keysmétodo para iterar sobre las claves del hash; y el 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 { diga  $par ;}# SALIDA: # tres => 3 # uno => 1 # dos => 2para  %palabra a número . kv -> $clave , $valor { diga  "$clave: $valor" }# SALIDA: # tres: 3 # uno: 1 # dos: 2para  %palabra a número . claves -> $clave { decir  "$clave => " ~ %palabra a número { $clave };}# SALIDA: # tres => 3 # uno => 1 # dos => 2

Sin embargo, los iteradores se pueden utilizar y definir explícitamente. Para cualquier tipo iterable, existen varios métodos que controlan diferentes aspectos del proceso de iteración. Por ejemplo, iteratorse supone que el método debe devolver un Iteratorobjeto y pull-onese supone que debe producir y devolver el siguiente valor si es posible, o devolver el valor centinela IterationEndsi no se pueden producir 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  := @values ​​. iterador ; # agarrar iterador para @valuesbucle { mi  $valor  := $it . tirar uno ; # tomar el siguiente valor de la iteración  al final  si  $value =:= IterationEnd ; # detenernos si llegamos al final de la iteración,  digamos  $valor ;}# SALIDA: # 1 # 2 # 3

Todos los tipos iterables en Raku componen el Iterablerol, Iteratorel rol o ambos. Es Iterablebastante simple y solo requiere que iteratorla clase que lo compone lo implemente. Es Iteratormás complejo y proporciona una serie de métodos como pull-one, que permite una operación de iteración más precisa en varios contextos, como agregar o eliminar elementos, u omitirlos para acceder a otros elementos. Por lo tanto, cualquier clase definida por el usuario puede soportar 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 repite:

subconjunto  Hebra  de  Str  donde { . partido ( /^^ <[ACGT]>+ $$/ ) y . caracteres  %% 3 }; la clase  ADN  hace  Iterable { tiene  $.chain ; método  nuevo ( Strand:D  $cadena ) { self . bendecir:  : $cadena }   iterador del método ( DNA:D: ){ $.chain . peine . rotor ( 3 ). iterador }};para  el ADN . nuevo ( 'GATTACATA' ) { . decir}# SALIDA: # (GAT) # (TAC) # (ATA)digamos  ADN . nuevo ( 'GATTACATA' ). mapa (*. unirse ). unirse ( '-' ); # SALIDA: # GAT-TAC-ATA

La Repeaterclase se compone de los roles Iterabley Iterator:

clase  Repetidor  hace  Iterable  hace  Iterador { tiene  Cualquier  $.item  es  requerido ; tiene  Int  $.times  es  obligatorio ; tiene  Int  $!count = 1 ;   método  múltiple nuevo ( $elemento , $veces ) { self . bendecir:  : $artículo , : $veces ; }   iterador del método { self } método  pull-one (--> Mu ){ if  $!count <= $!times { $!count += 1 ; devolver  $!artículo } else { retornar  fin de iteración } }}para  repetidor . nuevo ( "Hola" , 3 ) { . decir}# SALIDA: # Hola # Hola # Hola

Rubí

Ruby implementa iteradores de manera bastante diferente; todas las iteraciones se realizan pasando cierres 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 mapeo de funciones, filtros y 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 | norte | pone fin    

...y...

para n en 0 ... 42 pone n fin     

o incluso más corto

42 . veces lo hacen | norte | pone fin    

Ruby también puede iterar sobre listas fijas usando Enumerators y llamando a su #nextmétodo o haciendo a para cada una de ellas, como se indicó anteriormente.

Óxido

Rust hace uso de iteradores externos en toda la biblioteca estándar, incluso en su forbucle, que implícitamente llama al next()método de un iterador hasta que se consume. El bucle más básico, forpor ejemplo, itera sobre un Rangetipo:

para i en 0 .. 42 { println! ( "{}" , i ); } // Imprime los números del 0 al 41      

Específicamente, el forbucle llamará al into_iter()método de un valor, que devuelve un iterador que a su vez produce los elementos del bucle. El forbucle (o de hecho, 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 Testá el tipo de elemento).

Todas las colecciones proporcionadas por la biblioteca estándar implementan el IntoIteratorrasgo (lo que significa que definen el into_iter()método). Los propios iteradores implementan el Iteratorrasgo, lo que requiere definir el next()método. Además, a cualquier tipo de implementación Iteratorse le proporciona automáticamente una implementación IntoIteratorque regresa por sí misma.

Los iteradores admiten varios adaptadores ( ,,,, map()etc. ) como métodos proporcionados automáticamente por el rasgo.filter()skip()take()Iterator

Los usuarios pueden crear iteradores personalizados creando un tipo que implemente el Iteratorrasgo. Las colecciones personalizadas pueden implementar el IntoIteratorrasgo y devolver un tipo de iterador asociado para sus elementos, lo que permite su uso directamente en forbucles. A continuación, el Fibonaccitipo implementa un iterador personalizado e ilimitado:

estructura  Fibonacci ( u64 , u64 ); impl Fibonacci { pub fn nuevo () -> Yo { Yo ( 0 , 1 ) } }          iterador impl para Fibonacci { tipo elemento = u64 ;         fn  next ( & mut self ) -> Opción < Self :: Elemento > { let next = self . 0 ; ser . 0 = yo . 1 ; ser . 1 = yo . 0 + siguiente ;                Algunos ( siguiente ) } } let fib = Fibonacci :: nuevo (); para n en fib . saltar ( 1 ). paso a paso ( 2 ). tomar ( 4 ) { println! ( "{n}" ); } // Imprime 1, 2, 5 y 13        

Ver también

Referencias

  1. ^ Gatcomb, Joshua (16 de junio de 2005). "Comprensión y uso de iteradores". Perl.com . Consultado el 8 de agosto de 2012 . Un iterador definido por el usuario normalmente toma la forma de una referencia de código que, cuando se ejecuta, calcula el siguiente elemento de una lista y lo devuelve. Cuando el iterador llega al final de la lista, devuelve un valor acordado.
  2. ^ ab 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 . Consultado el 8 de agosto de 2012 . Los iteradores se introdujeron como construcciones para permitir recorrer estructuras de datos abstractas sin revelar su representación interna.
  3. ^ Álex Allain. "Iteradores STL". Cprogramming.com: su recurso para C y C++ . Consultado el 8 de agosto de 2012 . Puede pensar que un iterador apunta a un elemento que forma parte de un contenedor más grande de elementos.
  4. ^ ab "Diferencia entre un iterador externo y un iterador interno". CareerRide.COM. 2009-04-03. Archivado desde el original el 19 de septiembre de 2012 . Consultado el 8 de agosto de 2012 . Las funciones miembro de la clase que tiene la lógica de iteración implementan un iterador interno. Un iterador externo se implementa mediante una clase separada que se puede adjuntar al objeto que tiene lógica de iteración. La ventaja del iterador externo es que muchos iteradores se pueden activar simultáneamente en el mismo objeto o existente.{{cite web}}: Mantenimiento CS1: bot: estado de la URL original desconocido ( enlace )
  5. ^ 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 desde el 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 hacen distinciones sutiles entre los dos.{{cite web}}: Mantenimiento CS1: bot: estado de la URL original desconocido ( enlace )
  6. ^ ab Freeman, Eric; Hombre libre, Elisabeth; Kathy, Sierra; Bert, Bates (2004). Hendrickson, Mike; Loukides, Mike (eds.). Patrones de diseño Head First (libro de bolsillo) . vol. 1. O'REILLY. pag. 338.ISBN 978-0-596-00712-6. Consultado el 9 de agosto de 2012 .
  7. ^ "Glosario: documentación de Python 3.8.4" . Consultado el 15 de julio de 2020 .
  8. ^ Vecerina, Iván (1 de febrero de 2006). "índice vs iterador". BYTES. Archivado desde el original el 9 de agosto de 2012 . Consultado el 8 de agosto de 2012 . Un índice sólo se puede utilizar para contenedores que (eficientemente) admiten acceso aleatorio (es decir, acceso directo a un elemento en una posición determinada). Un iterador es un concepto más general. Los iteradores ofrecen un recorrido eficiente de listas vinculadas, archivos y otras estructuras de datos. A menudo conduce a la generación de código más eficiente.{{cite web}}: Mantenimiento CS1: bot: estado de la URL original desconocido ( enlace )
  9. ^ Kevin Waterson. "C++ Iteratoren: Iterator-Kategorien" (en alemán). cppreference.com . Consultado el 9 de agosto de 2012 .
  10. ^ Kevin Waterson. "Iteradores: conceptos". sgi . Consultado el 9 de agosto de 2012 .
  11. ^ larsmans (6 de marzo de 2011). "Tipos de iterador: salida, entrada, avance o iterador de acceso aleatorio". desbordamiento de pila. Archivado desde el original el 8 de agosto de 2012 . Consultado el 9 de agosto de 2012 .{{cite web}}: Mantenimiento CS1: bot: estado de la URL original desconocido ( enlace )
  12. ^ Kevin Waterson. "Introducción a SPL: Introducción a la biblioteca PHP estándar (SPL)". PHPPRO.ORG . Consultado el 9 de agosto de 2012 .
  13. ^ Collier, Andrés. "Iteradores en R". Archivado desde el original el 18 de octubre de 2018 . Consultado el 16 de noviembre de 2013 .
  14. ^ "Clase de plantilla concurrent_unordered_set". Intel Threading Building Blocks para código abierto. Archivado desde el original el 1 de mayo de 2015 . Consultado el 9 de agosto de 2012 . •Los tipos de iterador iterator y const_iterator son de la categoría de iterador directo
  15. ^ abcde Albahari, José. C# 10 en pocas palabras . O'Reilly. ISBN 978-1-098-12195-2.
  16. ^ abcde Skeet, Jon. C# en profundidad . Manning. ISBN 978-1617294532.
  17. ^ abcde 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 utilizando Visual Studio Code . Paquete. ISBN 978-1-098-12195-2.
  18. ^ abcdefg Bloch, Joshua (2018). "Java efectivo: Guía del lenguaje de programación" (tercera ed.). Addison-Wesley. ISBN 978-0134685991.
  19. ^ "java.util: Iterador de interfaz <E>: Resumen del método". Oráculo . Consultado el 8 de agosto de 2012 .
  20. ^ "Registro de cambios de PHP 4". El grupo PHP. 2000-02-20 . Consultado el 13 de octubre de 2015 .
  21. ^ Interno se refiere al hecho de que la interfaz no se puede implementar en scripts PHP, solo en la fuente C (lenguaje de programación) .
  22. ^ "La interfaz transitable". El grupo PHP . Consultado el 13 de octubre de 2015 .
  23. ^ "Iteradores". El grupo PHP . Consultado el 13 de octubre de 2015 .
  24. ^ "Registro de cambios de PHP 5". El grupo PHP. 2013-06-20 . Consultado el 13 de octubre de 2015 .

enlaces externos