Iterador

En computación, un objeto que permite recorrer un contenedor.

En programación de computadoras, un iterador se refiere al objeto que permite al programador recorrer un contenedor, (una colección de elementos) particularmente listas.[1][2][3]

Varios tipos de iteradores se suministran frecuentemente a través de una interfaz del contenedor. La interfaz y la semántica de un determinado iterador suelen ser fijas. Un iterador sigue una ruta y da acceso a elementos de datos del contenedor, pero no realiza iteración (es decir, no tiene total libertad, como sugiere su terminología). Un iterador se comporta como el cursor de una base de datos. Los iteradores se empezaron a utilizar en el lenguaje de programación CLU en 1974.

Descripción

editar

Iteradores externos y un patrón iterador

editar

Un iterador externo puede ser imaginado como un tipo de puntero que tiene dos operaciones primarias: referenciar un elemento particular en la colección de objetos y modificarse para apuntar al siguiente elemento. También debe existir una manera de crear un iterador para apuntar a algún primer elemento así como alguna manera de determinar cuando el iterador ha agotado todos los elementos en el contenedor. Dependiendo del lenguaje y del uso deseado, los iteradores también pueden proporcionar operaciones adicionales o mostrar comportamientos diferentes.

El propósito primario de un iterador es permitir que un usuario procese cada elemento de un contenedor mientras aísla al usuario de la estructura interna de un contenedor. Esto permite que el contenedor almacene elementos de la manera que él desee permitiendo que el usuario trate como si fuera una simple lista o secuencia. Una clase iterador normalmente se proyecta en estrecha coordinación con la clase contenedor correspondiente. Normalmente, el contenedor proporciona los métodos para crear iteradores.

Generadores

editar

Una manera de implementar iteradores es utilizar un tipo especial de su subrutina, conocido como un generador, que puede producir valores para quien lo llama varias veces (en lugar de devolver sólo uno).

A continuación se puede ver un ejemplo de un generador que devuelve los números de Fibonacci mediante la declaración siguiente código (en Python):

 def fibonacci():
     a, b = 0, 1
     while True:
         yield a
         a, b = b, a+b

 for numero in fibonacci():  # Utilización de generador como iterador
     print(numero)

Iteradores implícitos

editar

Algunos lenguajes orientados a objetos como C#, Delphi (versiones más recientes), Go (lenguaje de programación), Java , Lua, Perl, Python, Ruby, ofrecen una manera intrínseca de iteración a través de elementos de un objeto contenedor sin la introducción de un objeto iterador explícito. Un objeto iterador real puede existir en realidad, pero si existe, no se expone dentro del código fuente del lenguaje.

Los iteradores implícitos se manifiestan generalmente por una declaración "foreach" (u otra equivalente), como en el siguiente ejemplo en Python:

for valor in iterable:
    print(valor)

Otras veces pueden ser creados por un objeto de la colección, como en este ejemplo en Ruby:

iterable.each do |valor|
  puts valor
end

Este estilo de iteración es llamado a veces "iteración interna" debido a que su código se ejecuta completamente dentro del contexto del objeto iterable (que controla todos los aspectos de la iteración) y el programador sólo proporciona la operación para ejecutar en cada paso (utilizando una función anónima).

Los lenguajes que soportan comprensión de lista 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 for persona in lista if persona.femenino]

A veces la naturaleza oculta implícita es sólo parcial. El lenguaje C++ tiene pocas plantillas de función, como for_each(), que permiten la iteración implícita similar. Sin embargo, todavía requieren objetos interactivos explícitos como su entrada inicial. Una vez inicializada la iteración subsiguiente ocurre implícitamente sin el uso continuado de cualquier objeto iterador expuesto.

En diferentes lenguajes de programación

editar

Como un patrón para recorrer listas, conjuntos, mapas, etc:

Es lo que usted llamaría "cursor" si escribiera procedimientos almacenados en SQL. Por lo tanto, no es un concepto nuevo o extraordinariamente difícil de manipular.

Si se sabe que la List es un ArrayList, entonces no hay problemas en el uso del índice en vez de utilizar un Iterator. Para todos los demás tipos (LinkedList, Set, Map, etc.) hay que usar el Iterator.

Y de todos modos se continúa usando el for:

// digamos que la colección sea una colección BlaBleBli
for (Iterator it = coleccion.iterator() it.hasNext()) {
  BlaBleBli obj = (BlaBleBli) it.next();
}

En Java 5, el iterador puede incluso esconderse:

 for (BlaBleBli obj: coleccion) {

En el siguiente ejemplo tenemos la clase principal MenuItem que simplemente es un ítem de un menú que tiene un nombre, este podría ser un menú que aparecería en la sección de menú de un sitio, por ejemplo:

class MenuItem {
  String nombre;
  MenuItem(String nombre) {
    this.nombre = nombre;
  }
}

interface Iterator {
  boolean hasNext();
  Object next();
}

public class MenuIterator implements Iterator {
  MenuItem[] items;
  int posicion = 0;

  public MenuIterator(MenuItem[] items) {
    this.items = items;
  }

  public Object next() {
    MenuItem menuItem = items[posicion];
    posicion++;
    return menuItem;
  }

  public boolean hasNext() {
    if (posicion >= items.length || items[posicion] == null) {
      return false;
    }
    return true;
  }
}

Python

editar

Los iteradores en Python son una parte fundamental del lenguaje y en muchos casos pasan desapercibidos, pues se utilizan implícitamente en la declaración for(foreach), en comprensiones de listas y en expresiones generadoras. Todos los tipos de colección nativos de Python soportan iteración, así como muchas clases que son parte de la biblioteca estándar. En el siguiente ejemplo se muestra una iteración implícita típica sobre una secuencia:

for valor in secuencia:
    print(valor)

Los diccionarios de Python (una forma de matriz asociativa) también pueden ser iterados directamente, cuando se devuelven las claves del diccionario; o el método items de un diccionario puede ser iterado sobre donde produce pares clave, valores correspondientes como una tupla:

for clave in diccionario:
    valor = diccionario[clave]
    print(clave, valor)
for clave, valor in diccionario.items():
    print(clave, valor)

Los iteradores, sin embargo, se pueden utilizar y definir explícitamente. Para cualquier tipo de secuencia iterable o clase, la función nativa iter() se utiliza para crear un objeto iterador. El objeto iterador puede entonces ser iterado con la función next(), que utiliza el método __next__() internamente, que devuelve el siguiente elemento en el contenedor (La declaración anterior es aplicable en Python 3.x. En Python 2.x, es equivalente el método next()). Una excepción StopIteration será lanzada cuando no queden más los elementos. El siguiente ejemplo muestra una iteración sobre una cadena equivalente utilizando iteradores explícitos:

it = iter(secuencia)
while True:
    try:
        valor = it.next() # en Python 2.x
        valor = next(it) # en Python 3.x
    except StopIteration:
        break
    it = iter(it)
    print(valor)

Cualquier clase definida por el usuario puede soportar iteración estándar (implícita o explícita), mediante la definición de un método __iter__() que devuelve un objeto iterador. El objeto iterador, entonces, debe definir un método __next__() que devuelve el siguiente elemento y un método __iter__() que devuelve el siguiente objeto iterador a utilizar.

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

El foreach fue introducido en la versión 4.0 de PHP y hecho compatible con los objetos como valores en 4.0 Beta 4. Sin embargo, el soporte a iteradores se añadió en PHP 5 a través de la introducción de la interfaz interna atravesable (no implementable en el código PHP, sino en lenguaje C.

Las dos interfaces principales para la implementación de código PHP que permiten que los objetos sean iterados a través del bucle foreach son Iterator y IteratorAggregate. Este último no requiere que la clase de implementación declare todos los métodos requeridos, sino que implementa un método a para acceder (getter) (getIterator) que devuelve una instancia de Traversable . La Biblioteca estándar de PHP proporciona varias clases para trabajar con iteradores especiales. PHP también soporta generadores desde la versión 5.5. [4]

La implementación más simple es envolviendo un arreglo, esto puede ser útil para sugerencia de tipo y ocultación de información.

namespace Wikipedia\Iterator;

final class ArrayIterator extends \Iterator {

    private $array;

    public function __construct(array $array) {
        $this->array = $array;
    }

    public function rewind() {
        echo 'rebobinado' , PHP_EOL;
        reset($this->array);
    }

    public function current() {
        $value = current($this->array);
        echo "actual: {$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() {
        $valid = $this->current() !== false;
        echo 'validar: ' , ($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:

  1. $iterator-> rewind() asegura que la estructura interna comienza desde el principio.
  2. $iterator-> valid() devuelve true 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 "false" y el bucle se anula.

El siguiente ejemplo ilustra una clase PHP que implementa la interfaz Traversable, que podría ser envuelta en una clase IteratorIterator para actuar sobre los datos antes de ser devuelta al bucle foreach. El uso junto con la constante MYSQLI_USE_RESULT 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 PDOStatement implementa 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 retornada por el metodo implementa la interfaz interna ''Traversable''.
foreach ($mysqli->query('SELECT `a`, `b`, `c` FROM `table`', MYSQLI_USE_RESULT) as $row) {
    //Actúa en la fila devuelta, que es un arreglo asociativo.
}

Véase también

editar

Referencias

editar
  1. Joshua, Gatcomb. «A user-defined iterator usually takes the form of a code reference that, when executed, calculates the next item in a list and returns it. When the iterator reaches the end of the list, it returns an agreed-upon value.». Perl.com. Consultado el 8 de agosto de 2012. 
  2. = Stephen M., Watt. «Iterators were introduced as constructs to allow looping over abstract data structures without revealing their internal representation.» (PDF). The University of Western Ontario, Department of Computer Science. Consultado el 8 de agosto de 2012. 
  3. Alex Allain. «You can think of an iterator as pointing to an item that is part of a larger container of items.». Cprogramming.com - Your resource for C and C++. Consultado el 8 de agosto de 2012. 
  4. «PHP 5 ChangeLog». 20 de junio de 2013. Consultado el 13 de octubre de 2015.