15 de mayo de 2017

Consideraciones adicionales.

   Una de las aplicaciones más útiles de las colas, las colas de prioridad y de las listas vinculadas es la simulación.

   Un programa de simulación modela una situación del mundo real para aprender u obtener algo de ella. En una simulación, cada objeto y acción del mundo tiene su representación en el programa.

   Si la simulación es precisa, el resultado del programa reflejará el resultado de las acciones que se simulan, por lo que será posible comprender lo que ocurre en una situación del mundo real sin la necesidad de observar su ocurrencia en el mundo real.

   Algunos de los contextos en los que es posible aplicar técnicas de simulación utilizando las estructuras de datos anteriormente mencionadas son:
  • Bancos.
  • Líneas aéreas.
  • Casetas de cobro de autopistas.
  • Terminal de autobuses.
  • Líneas de espera en general.
  • Un largo etcétera.
   Las listas enlazadas completan el espectro de las estructuras de datos lineales que se estudiarán en este blog, mismo que dio inició con las pilas. Debe notar que en éste sentido se ha ido avanzando de lo particular hacia lo general, ya que como pudo apreciarse, las listas enlazadas son las estructuras de datos que menos restricciones tienen en su definición y operaciones.

   Tanto los usos como las aplicaciones de las pilas, las colas de espera y las listas enlazadas son prácticamente infinitas, ya que están limitadas únicamente por la imaginación o la capacidad del programador.
 
Iteradores.
    Un iterador es un objeto que permite al programador recorrer un contenedor de una manera determinada. Puede verse también como la entidad que le permite a un programa "caminar" o recorrer una estructura de datos desde el exterior.
 
Diagrama de clases de la estructura del patrón de diseño de un iterador [Wikipedia].
 
    En el contexto en que se ha venido manejado en el blog, un iterador regresará el valor almacenado (dato) en los nodos internos de la estructura de datos.

    Iteradores en Java.
    El API de Java proporciona distintas colecciones que pueden ser recorridas con iteradores. Cualquier clase que implemente la interfaz Iterator debe ser capaz de regresar un iterador que permita recorrer los elementos que almacena.

    A modo de ejemplo se hará una modificación al Ejemplo Lista estudiado en la entrada correspondiente a la implementación. Por simplicidad y para tener un ejemplo completamente funcional, se proporcionará como una carpeta comprimida que contiene todos los archivos necesarios para su ejecución. Sólo se resaltarán los cambios en dos clases, y se deja como ejercicio al lector hacer la comparación correspondiente con el ejemplo original:
  1. Lista:
    1. Linea 10: implementación de la interfaz Iterator.
    2. Línea 13: objeto que se regresará como iterador (referencia externa).
    3. Línea 22: inicialización del iterador en el constructor.
    4. Líneas 104-109: implementación del método next de la interfaz Iterator. Este método se encarga de obtener el siguiente dato de la lista, regresarlo y avanzar hacia el siguiente nodo.
    5. Líneas 111-113: implementación del método hasNext de la interfaz Iterator. Este método se encarga de determinar si hay más elementos (true) o no (false) en la estructura de datos.
    6. Líneas 115-118: implementación del método iterator de la interfaz Iterator. Este método se encarga de iniciar un iterador al principio de la estrictura de datos para poderla recorrer desde el exterior. Note que el método regresa una referencia al mismo objeto (this) ya que su clase (Lista) implementa la interfaz Iterator y en ella están definidos los métodos requeridos para gestionar el iterador.
  2. PruebaLista:
    1. Línea 5: inclusión de la interfaz Iterator del paquete java.util.
    2. Líneas 19-22: uso del iterador implementado en la clase Lista.

    Adicional a la interfaz Iterator, existe también en el API la interfaz Iterable (más reciente), que permite hacer uso de ciclos del tipo for-each. El Ejemplo ListaIterable muestra su implementación y uso y, dado que los cambios son menores y relativamente simples respecto del ejemplo anterior, se deja su análisis y estudio como ejercicio para el lector.

    Iteradores en C++. 

    La idea de un iterador debería ser el poder recorrer la colección o estructura de datos en cuestión desde el exterior sin tener que conocer los detalles de su implementación. En este sentido, un iterador será un objeto que haga referencia a un elemento dentro del contenedor (la lista) y, dado que finalmente es un apuntador, puede ser utilizado para acceder al elemento al que se refiere y para moverse a través de todos los elementos del contenedor.

    El Ejemplo lista_iterador se basa en general en el Ejemplo lista estudiado con anterioridad. Ahora bien, el primero ha sido rediseñado en su totalidad, por lo que podría resultar conveniente al lector comprender y analizar antes el Ejemplo lista2.

    Para el ejemplo en turno se han incorporado también algunas de las características más recientes del lenguaje C++, por lo que los cambios más sobresalientes se enuncian a continuación, manteniendo el énfasis en la implementación respecto al iterador:

  • Implementación básica del iterador (líneas 67-94):
    • Apuntadores que representan las referencias la nodo actual y anterior de la lista (líneas 69-70).
    • Constructores y mecanismos de inicialización del iterador (líneas 73-74).
    • Implementación de los tres operadores fundamentales (se puede y deben implementar otros):
      • Operador de preincremento: ¿qué debe hacer el iterador cuando se incremente? (líneas 77-83).
      • Operador de diferencia: ¿cómo sabe un iterador que es distinto de otro? (líneas 86-88).
      • Operador de desreferencia: ¿qué regresa el iterador cuando se acceda a lo que apunta? (líneas 91-93).
  • Incorporación de mecanismos que permitan obtener un iterador al inicio de la colección y poder determinar su fin (líneas 184-186).
  • Uso del iterador:
    • Uso del esquema para cada (for-each) en las líneas 206-207.
    • Uso del esquema tradicional del iterador en las líneas 211-216.
    El ejemplo en turno incorpora algunas características nuevas en las que vale la pena documentarse, como el uso de noexcept, inline, auto, etc., se deja como ejercicio para el lector su documentación.
 
    Finalmente, cabe mencionar que una implementación más robusta de iteradores requiere de un conocimiento más amplio, sobre todo si se espera que nuestra estructura de datos pudiese ser utilizada por otros programadores o por los algoritmos de la biblioteca estándar. Lo expuesto aquí es sólo una implementación básica, ya que existen distintos tipos de iteradores, operadores de acceso, consideraciones de recorrido, etcétera. Un punto inicial y de buena referencia puede ser la sección de iteradores de cplusplus, en dónde podrá encontrar además la información referente a los otros aspectos utilizados en el ejemplo.