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 una implementación de 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 de un cursor de 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 a elementos ) y puede cambiar su estado interno para proporcionar acceso al siguiente elemento ( recorrido de elementos ). [4] 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 una funcionalidad adicional.

Un iterador permite a un consumidor procesar cada elemento de una colección mientras aísla al consumidor de la estructura interna de la colección. [2] La colección puede almacenar elementos de cualquier manera 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, a un contador de bucle también se lo denomina iterador de bucle. Sin embargo, un contador de bucle solo proporciona la funcionalidad de recorrido y no la funcionalidad de acceso a elementos.

Generador

Una forma de implementar un iterador es a través de una forma restringida de corrutina , conocida como generador . A diferencia de una subrutina , una corrutina generadora puede generar valores para su invocador 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 conservan su estado local entre invocaciones, son particularmente adecuados para iteradores complicados y con estado, como los traversers 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 iteradores : 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  rango ( límite ):  rendimiento  a  a ,  b  =  b ,  a  +  bpara  el 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 suele adoptar funciones anónimas ) que recorre una colección mientras aplica una función a cada elemento. Por ejemplo, mapla función de Python aplica una función definida por el llamador a cada elemento:

dígitos  =  [ 0 ,  1 ,  2 ,  3 ,  4 ,  5 ,  6 ,  7 ,  8 ,  9 ]dígitos_cuadrados  =  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. Un objeto iterador puede existir, pero no está representado en el código fuente. [4] [6]

Un iterador implícito a menudo se manifiesta 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 fin    

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

Los lenguajes que admiten comprensiones de listas o construcciones similares también pueden hacer uso de 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  persona . masculino ]

A veces, la naturaleza oculta implícita es solo 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 flujo incluyen lenguajes basados ​​en datos , como AWK y sed .

Contraste con la 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 se puede utilizar la indexación con colecciones, el uso de iteradores puede tener ventajas como: [8]

La capacidad de modificar una colección mientras se itera a través de sus elementos se ha vuelto necesaria en la programación orientada a objetos moderna , donde las interrelaciones entre los objetos y los efectos de las operaciones pueden no ser obvias. Al usar 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.

En el caso de las colecciones que pueden mover sus datos en la memoria, la única forma de no invalidar el iterador es, en el caso de la colección, llevar un registro de todos los iteradores activos en ese momento y actualizarlos sobre la marcha. Dado que la cantidad de iteradores en un momento dado puede ser arbitrariamente grande en comparación con el tamaño de la colección vinculada, actualizarlos todos perjudicará drásticamente la garantía de complejidad de las operaciones de la colección.

Una forma alternativa de mantener el número de actualizaciones limitado en relación con el tamaño de la colección sería utilizar un tipo de mecanismo de identificador, 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 efectuar un doble puntero a continuación para acceder al elemento de datos real. Esto no suele ser deseable, porque muchos algoritmos que utilizan los iteradores invocan la operación de acceso a datos de los iteradores con más frecuencia que el método avanzado. Por lo tanto, es especialmente importante tener iteradores con un acceso a datos muy eficiente.

En definitiva, siempre se trata de un equilibrio entre la seguridad (los iteradores siguen siendo siempre válidos) y la eficiencia. La mayoría de las veces, la seguridad añadida no compensa el precio que hay que pagar por ella en términos de eficiencia. Si se necesita la estabilidad de los iteradores, sería mejor utilizar una colección alternativa (por ejemplo, una lista enlazada simple en lugar de un vector) (globalmente más eficiente).

Clasificación

Categorías

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

Tipos

Los tipos de iteradores se definen en distintos lenguajes o bibliotecas que se utilizan con estos lenguajes. Algunos de ellos son [12]

En diferentes lenguajes de programación

.NETO

Los iteradores en .NET Framework (es decir, C#) se denominan "enumeradores" y se representan mediante 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 Reset() y un método opcional , [15] : 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 típicamente al llamar 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 contenedoras típicamente implementan esta interfaz. Sin embargo, la declaración foreach en C# puede operar en cualquier objeto que proporcione dicho método, incluso si no implementa IEnumerable( tipado pato ). [17] : 89  Ambas interfaces se expandieron 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 = list . GetEnumerator (); mientras ( iter . MoveNext ()) Console . WriteLine ( iter . Current );     // versión implícita foreach ( MyType valor en lista ) Console . WriteLine ( valor );     

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

C++

El lenguaje C++ hace un amplio uso de 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 de posibilidades crecientes. Todos los tipos de plantilla de contenedor estándar proporcionan iteradores de una de estas categorías. Los iteradores generalizan punteros a elementos de una matriz (que de hecho se pueden usar 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 utilizan para hacer referencia al elemento al que apunta el iterador y los operadores de aritmética de punteros como se utilizan para modificar iteradores en el recorrido de un contenedor.*->++

El recorrido mediante iteradores suele implicar un único iterador variable y dos iteradores fijos que sirven para delimitar un rango que se va a recorrer. La distancia entre los iteradores limitadores, 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 limitador inferior "apunta" al primer elemento del rango, mientras que el iterador limitador superior no apunta a ningún elemento del rango, sino más bien justo más allá del final del rango. Para el recorrido de 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 en absoluto, 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 > 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'.    for ( auto it = items . begin (); it != items . end (); ++ it ) { // Iterar a través de 'items'. std :: cout << * it ; // E imprimir el valor de 'items' para el índice actual. } // En C++11, se puede hacer lo mismo sin usar ningún iterador: for ( auto x : items ) { std :: cout << x ; // Imprimir el valor de cada elemento 'x' de 'items'. }                       // Ambos bucles for imprimen "529".

Los tipos de iteradores son independientes de los tipos de contenedores con los que se utilizan, aunque a menudo se utilizan ambos en conjunto. La categoría del iterador (y, por lo tanto, las operaciones definidas para él) normalmente depende del tipo de contenedor; por ejemplo, las matrices o los vectores proporcionan iteradores de acceso aleatorio, pero 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 realice un recorrido habitual (hacia adelante) en realidad hará el recorrido en orden inverso cuando se lo llame con iteradores inversos. La mayoría de los contenedores también proporcionan un const_iteratortipo independiente, para el cual las operaciones que permitirían cambiar los valores a los que se apunta no se definen 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 utilice a) se puede realizar utilizando 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 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 que apunten al mismo contenedor simultáneamente, las operaciones de modificación de 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 conducirá a un comportamiento indefinido, y dichos 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(), std::copy()y std::accumulate().

Cuando se utilizan, deben inicializarse con iteradores existentes, generalmente beginy end, que definen el rango en el que se produce la iteración. Pero no se expone ningún objeto iterador explícito a medida que avanza la iteración. Este ejemplo muestra el uso de for_each.

ContainerType < ItemType > 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.begin ( ), c.end (), ProcessItem ) ; // Un bucle de iteración for-each .   

Lo mismo se puede lograr usando std::copy, pasando un std::ostream_iteratorvalor como tercer iterador:

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

Desde C++11 , se puede utilizar la sintaxis de la función lambda para especificar la operación que se va a iterar en línea, lo que evita la necesidad de definir una función con nombre. A continuación, se muestra un ejemplo de iteración for-each con 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 ) { 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 contenedoras. Cada una Iteratorproporciona un método next()y , [18] : 294–295  y puede admitir opcionalmente un método [18] : 262, 266.  Los iteradores son creados por la clase contenedora correspondiente, normalmente por un método llamado . [19] [18] : 99  [18] : 217 hasNext()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(). [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 = list . iterator ( ); // Iterador<MyType> iter = list.iterator(); // en J2SE 5.0 while ( iter . hasNext ( )) { System . print ( iter . next ()); if ( iter . hasNext ( )) System . 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 necesita un avance sin acceso a los datos (es decir, para 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 ) hace que el iterador sea inutilizable. Un intento de obtener el siguiente elemento lanza la excepción. También se lanza una excepción si no quedan más elementos ( hasNext()previamente devolvió false).

Además, java.util.Listexiste una java.util.ListIteratorcon una API 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 bucle mejorado for, el ejemplo anterior se puede reescribir como

para ( MiTipo obj : lista ) { Sistema . out . print ( obj ); }      

Algunos contenedores también utilizan la Enumerationclase anterior (a partir de la versión 1.0). Esta proporciona métodos hasMoreElements()y nextElement(), pero no tiene métodos para modificar el contenedor.

Escala

En Scala , los iteradores tienen un amplio 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 amplio conjunto de métodos disponibles en la biblioteca de colecciones de Scala, como map, collect, filteretc., a menudo no es necesario tratar con iteradores directamente 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

importar scala . colección . JavaConversions . _ 

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 ámbito actual, insertan automáticamente llamadas a sí mismos en expresiones relevantes en el lugar apropiado para que se verifique su tipo cuando de otra manera no lo harían.

MATLAB

MATLAB admite iteraciones implícitas internas y externas utilizando matrices o arreglos "nativos" cell. En el caso de la 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,

% Define una matriz de números enteros myArray = [ 1 , 3 , 5 , 7 , 11 , 13 ];  para n = myArray % ... hacer algo con n disp ( n ) % Echo entero a la ventana de comandos fin      

recorre una matriz de números enteros utilizando 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 una matriz de salida correspondiente de manera implícita. Además, las funciones arrayfuny cellfunse pueden aprovechar para realizar operaciones personalizadas o definidas por el usuario sobre matrices y matrices "nativas" cell, respectivamente. Por ejemplo,

función simpleFun % Define 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 );  % Echo de la matriz resultante a 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 mediante la función incorporada arrayfun.

Como alternativa, puede ser deseable abstraer los mecanismos del contenedor de almacenamiento de matriz del usuario mediante la definición de una implementación personalizada de MATLAB orientada a objetos del Patrón Iterador. Una implementación de este tipo que admita la iteración externa se demuestra en el Patrón de diseño de elementos de MATLAB Central File Exchange: Iterador (conductual). 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 cellrealización de matriz unidimensional del 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 el recorrido explícito de listas hacia adelante con los hasNext()métodos next()y reset()para su uso en un whilebucle.

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, el soporte para iteradores se agregó en PHP 5 a través de 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 . El último no requiere que la clase implementadora declare todos los métodos requeridos, en su lugar implementa un método de acceso ( ) que devuelve una instancia de . La biblioteca estándar de PHP proporciona varias clases para trabajar con iteradores especiales. [23] PHP también admite generadores desde 5.5. [24]TraversableforeachIteratorIteratorAggregategetIteratorTraversable

La implementación más simple es envolviendo una matriz, esto puede ser útil para sugerencias de tipos y ocultación de información .

espacio de nombres  Wikipedia\Iterator ; clase  final ArrayIterator  extiende  \Iterator {  matriz privada  $array ;   función  pública __construct ( matriz  $matriz )  {  $this -> matriz  =  $matriz ;  }  función  pública rebobinar () :  void  {  echo  'rebobinando'  ,  PHP_EOL ;  restablecer ( $this -> array );  }  función  pública actual ()  {  $valor  =  actual ( $this -> array );  echo  "actual: { $valor } " ,  PHP_EOL ;  devolver  $valor ;  }  función  pública clave ()  {  $clave  =  clave ( $this -> array );  echo  "clave: { $clave } " ,  PHP_EOL ;  return  $clave ;  }  función  pública siguiente ()  {  $valor  =  siguiente ( $este -> matriz );  echo  "siguiente: { $valor } " ,  PHP_EOL ;  devolver  $valor ;  }  función  pública válida () :  bool  {  $válido  =  $este -> 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 comience 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 en la estructura interna.
  6. $iterator->valid()devuelve falso y se cancela el bucle.

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 clase 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_de_base_de_datos' );// La clase \mysqli_result que se devuelve mediante 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 utilizan de forma implícita en la declaración for( foreach ), en las comprensiones de listas y en las expresiones de generador . Todos los tipos de colección integrados 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 items()se puede iterar el método de un diccionario donde produce pares clave-valor correspondientes como una tupla:

para  clave  en  diccionario :  valor  =  diccionario [ clave ]  print ( clave ,  valor )
para  clave ,  valor  en  el diccionario.items () : print ( clave , valor )  

Sin embargo, los iteradores se pueden utilizar y definir explícitamente. Para cualquier tipo o clase de secuencia iterable, se utiliza la función incorporada iter()para crear un objeto iterador. El objeto iterador se puede iterar luego 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  True :  try :  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. El objeto iterador 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 APIs de iteración como la forsentencia, 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 { digamos  $valor}# SALIDA: # 1 # 2 # 3

Los hashes de Raku también se pueden iterar directamente; esto produce Pairobjetos clave-valor. kvSe puede invocar el método 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-numero = 'uno' => 1 , 'dos' => 2 , 'tres' => 3 ; para  %palabra-a-numero -> $par { decir  $par ;}# SALIDA: # tres => 3 # uno => 1 # dos => 2para  %palabra-a-número . kv -> $clave , $valor { decir  "$clave: $valor" }# SALIDA: # tres: 3 # uno: 1 # dos: 2para  %palabra-a-número .keys -> $key { decir " $key => " ~ %palabra-a-número { $key }; }# 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-oneque 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  @values ​​= 1 , 2 , 3 ; mi  $it  := @values ​​. iterator ; # toma el iterador para @valuesloop { my  $value  := $it . pull-one ; # toma el siguiente valor de la iteración  last  if  $value =:= IterationEnd ; # para si llegamos al final de la iteración  say  $value ;}# SALIDA: # 1 # 2 # 3

Todos los tipos iterables en Raku componen el Iterablerol, Iteratorel rol o ambos. El Iterablees bastante simple y solo requiere iteratorque el sea implementado por la clase que lo compone. El Iteratores más complejo y proporciona una serie de métodos como pull-one, que permite una operación más fina de iteración en varios contextos, como agregar o eliminar elementos, o saltearlos para acceder a otros elementos. Por lo tanto, cualquier clase definida por el usuario puede admitir la iteración estándar al componer estos roles e implementar los métodos iteratory/o pull-one.

La DNAclase representa una cadena de ADN y la implementa iteratormediante la composición de la Iterablefunción. La cadena de ADN se divide en un grupo de trinucleótidos cuando se itera sobre ella:

subconjunto  Hebra  de  Str  donde { . match ( /^^ <[ACGT]>+ $$/ ) y . chars  %% 3 }; clase  DNA  hace  Iterable { tiene  $.chain ; método  new ( Hebra:D  $chain ) { self . bless:  : $chain }  método  iterador ( DNA:D: ){ $.chain . comb . rotor ( 3 ). iterator }};para  el ADN . nuevo ( 'GATTACATA' ) { . decir}# SALIDA: # (GAT) # (TAC) # (ATA)decir  ADN . nuevo ( 'GATTACATA' ). mapa (*. unirse ). unirse ( '-' ); # SALIDA: # GAT-TAC-ATA

La Repeaterclase se compone tanto de los roles Iterablecomo Iteratorde:

clase  Repeater  hace  Iterable  hace  Iterator { tiene  Any  $.item  es  obligatorio ; tiene  Int  $.times  es  obligatorio ; tiene  Int  $!count = 1 ;   método  múltiple nuevo ( $item , $times ) { self . bless:  : $item , : $times ; }  método  iterador { self } método  pull-one (--> Mu ){ if  $!count <= $!times { $!count += 1 ; return  $!item } de lo contrario { devolver  IteraciónFin } }}para  repetidor . new ( "Hola" , 3 ) { . decir}# SALIDA: # Hola # Hola # Hola

Rubí

Ruby implementa los iteradores de una manera bastante diferente; todas las iteraciones se realizan mediante el paso de cierres de devolución de llamada a los métodos del contenedor; 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 do | n | pone n fin    

...y...

para n en 0 ... 42 pone n al final     

o incluso más corto

42. veces hace | n | pone n fin    

Ruby también puede iterar sobre listas fijas usando Enumerators y llamando a su #nextmétodo o haciendo un for each en 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 41      

En concreto, el forbucle llamará al into_iter()método de un valor, que devuelve un iterador que, a su vez, devuelve los elementos al bucle. El forbucle (o, de hecho, cualquier método que consuma el iterador) continúa hasta que el next()método devuelva un Nonevalor (las iteraciones que devuelven elementos devuelven un Some(T)valor, donde Tes el tipo de elemento).

Todas las colecciones proporcionadas por la biblioteca estándar implementan el IntoIteratoratributo (es decir, definen el into_iter()método). Los iteradores implementan el Iteratoratributo, lo que requiere definir el next()método. Además, a cualquier tipo que lo implemente Iteratorse le proporciona automáticamente una implementación IntoIteratorque lo retorna.

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 el Iteratoratributo. Las colecciones personalizadas pueden implementar el IntoIteratoratributo 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ímites:

estructura  Fibonacci ( u64 , u64 ); impl Fibonacci { pub fn new () -> Self { Self ( 0 , 1 ) } }          impl Iterador para Fibonacci { tipo Item = u64 ;         fn  next ( & mut self ) - > Opción < Self :: Elemento > { let next = self.0 ; self.0 = self.1 ; self.1 = self.0 + next ;                Algunos ( siguiente ) } } sea ​​fib = Fibonacci :: new (); para n en fib.skip ( 1 ) .step_by ( 2 ) .take ( 4 ) { println! ( "{n}" ); } // Imprime 1, 2, 5 y 13        

Véase también

Referencias

  1. ^ Gatcomb, Joshua (16 de junio de 2005). "Understanding and Using Iterators" (Comprensión y uso de iteradores). Perl.com . Consultado el 8 de agosto de 2012. Un iterador definido por el usuario suele adoptar 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 la realización de bucles sobre estructuras de datos abstractas sin revelar su representación interna.
  3. ^ Alex Allain. "Iteradores STL". Cprogramming.com - Su recurso para C y C++ . Consultado el 8 de agosto de 2012. Puede pensar en un iterador como si apuntara 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 2012-09-19 . Consultado el 2012-08-08 . Un iterador interno se implementa mediante las funciones miembro de la clase que tiene la lógica de iteración. Un iterador externo se implementa mediante una clase separada que se puede adjuntar al objeto que tiene la lógica de iteración. La ventaja del iterador externo es que se pueden activar muchos iteradores simultáneamente en el mismo objeto o en el objeto existente.{{cite web}}: CS1 maint: bot: estado de 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 ambos.{{cite web}}: CS1 maint: bot: estado de URL original desconocido ( enlace )
  6. ^ ab Freeman, Eric; Freeman, Elisabeth; Kathy, Sierra; Bert, Bates (2004). Hendrickson, Mike; Loukides, Mike (eds.). Patrones de diseño Head First (libro de bolsillo) . Vol. 1. O'REILLY. pág. 338. ISBN 978-0-596-00712-6. Recuperado el 9 de agosto de 2012 .
  7. ^ "Glosario — Documentación de Python 3.8.4" . Consultado el 15 de julio de 2020 .
  8. ^ Vecerina, Ivan (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 solo se puede utilizar para contenedores que (de manera eficiente) 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 enlazadas, archivos y otras estructuras de datos. A menudo conduce a la generación de código más eficiente.{{cite web}}: CS1 maint: bot: estado de 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 iteradores: salida, entrada, iteradores de acceso directo e iteradores de acceso aleatorio". stackoverflow. Archivado desde el original el 8 de agosto de 2012. Consultado el 9 de agosto de 2012 .{{cite web}}: CS1 maint: bot: estado de URL original desconocido ( enlace )
  12. ^ Kevin Waterson. "Introducción a SPL: Introducción a la biblioteca PHP estándar (SPL)". PHPRO.ORG . Consultado el 9 de agosto de 2012 .
  13. ^ Collier, Andrew. «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". Bloques de construcción de subprocesos de Intel 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 pertenecen a la categoría de iterador hacia adelante .
  15. ^ abcde Albahari, José (2022). C# 10 en pocas palabras . O'Reilly. ISBN 978-1-098-12195-2.
  16. ^ abcde Skeet, Jon (23 de marzo de 2019). 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 mediante Visual Studio Code . Packt. 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: Interface Iterator<E>: Method Summary". Oracle . Consultado el 8 de agosto de 2012 .
  20. ^ "Registro de cambios de PHP 4". The PHP Group. 20 de febrero de 2000. 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 Traversable". The PHP Group . Consultado el 13 de octubre de 2015 .
  23. ^ "Iteradores". The PHP Group . Consultado el 13 de octubre de 2015 .
  24. ^ "Registro de cambios de PHP 5". The PHP Group. 20 de junio de 2013. Consultado el 13 de octubre de 2015 .

Enlaces externos