Iterador

En programación funcional , un iterador es una abstracción componible para procesar de forma incremental fragmentos de datos de entrada presentados secuencialmente de una manera puramente funcional . Con los iteradores, es posible transformar de forma diferida la forma en que un recurso emitirá datos, por ejemplo, convirtiendo cada fragmento de la entrada a mayúsculas a medida que se recuperan o limitando los datos solo a los primeros cinco fragmentos sin cargar todos los datos de entrada en la memoria. Los iteradores también son responsables de abrir y cerrar recursos, lo que proporciona una gestión de recursos predecible.

En cada paso, se presenta al iterador uno de los tres tipos posibles de valores: el siguiente fragmento de datos, un valor para indicar que no hay datos disponibles o un valor para indicar que el proceso de iteración ha finalizado. Puede devolver uno de los tres tipos posibles de valores para indicar al invocador qué se debe hacer a continuación: uno que signifique "detenerse" (y que contenga el valor de retorno final), uno que signifique "continuar" (y especifique cómo continuar) y uno que signifique "señalar un error". Los últimos tipos de valores representan en efecto los posibles "estados" de un iterador. Un iterador normalmente comenzaría en el estado "continuar".

Los iteradores se utilizan en Haskell y Scala (en el marco Play [1] y en Scalaz), y también están disponibles para F# . [2] Existen varias implementaciones ligeramente diferentes de iteradores. Por ejemplo, en el marco Play, involucran futuros para que se pueda realizar un procesamiento asincrónico.

Debido a que los iteradores son llamados por otro código que los alimenta con datos, son un ejemplo de inversión de control . Sin embargo, a diferencia de muchos otros ejemplos de inversión de control, como el análisis de XML SAX , el iterador conserva una cantidad limitada de control sobre el proceso. No puede volver atrás y mirar los datos anteriores (a menos que almacene esos datos internamente), pero puede detener el proceso limpiamente sin lanzar una excepción (el uso de excepciones como un medio de flujo de control , en lugar de para señalar un evento excepcional, a menudo es mal visto por los programadores [3] ).

Abstracciones comúnmente asociadas

Las siguientes abstracciones no son estrictamente necesarias para trabajar con iteradores, pero lo hacen más conveniente.

Enumeradores

Un enumerador es una abstracción conveniente para introducir datos en un iterador desde una fuente de datos arbitraria. Normalmente, el enumerador se encargará de cualquier limpieza de recursos necesaria asociada con la fuente de datos. Debido a que el enumerador sabe exactamente cuándo el iterador ha terminado de leer los datos, realizará la limpieza de recursos (como cerrar un archivo) exactamente en el momento adecuado, ni demasiado pronto ni demasiado tarde. Sin embargo, puede hacer esto sin necesidad de conocer la implementación del iterador o estar ubicado junto a ella, por lo que los enumeradores y los iteradores forman un ejemplo de separación de preocupaciones .

Enumerados

Un enumerado es una abstracción conveniente para transformar la salida de un enumerador o iterador y enviar esa salida a un iterador. Por ejemplo, un enumerado "map" mapearía una función sobre cada fragmento de entrada. [4]

Motivaciones

Los iteradores se crearon debido a problemas con las soluciones puramente funcionales existentes al problema de hacer que la entrada/salida sea componible pero correcta. La E/S perezosa en Haskell permitía que las funciones puras operaran sobre los datos en el disco como si estuvieran en la memoria, sin hacer E/S explícitamente en absoluto después de abrir el archivo - una especie de característica de archivo mapeado en memoria - pero debido a que era imposible en general (debido al problema de Halting ) que el tiempo de ejecución supiera si el archivo u otro recurso aún era necesario, se podían dejar abiertos números excesivos de archivos innecesariamente, lo que resultaba en el agotamiento del descriptor de archivo a nivel del sistema operativo . La E/S tradicional al estilo C , por otro lado, era de demasiado bajo nivel y requería que el desarrollador se preocupara por detalles de bajo nivel como la posición actual en el archivo, lo que obstaculizaba la componibilidad. Los iteradores y enumeradores combinan los beneficios de programación funcional de alto nivel de la E/S perezosa, con la capacidad de controlar recursos y detalles de bajo nivel cuando sea necesario que ofrece la E/S al estilo C. [5]

Ejemplos

Usos

Los iteradores se utilizan en el marco Play para enviar datos a conexiones Comet y WebSocket de larga ejecución a navegadores web .

Los iteradores también se pueden utilizar para realizar análisis incremental (es decir, análisis que no lee todos los datos en la memoria a la vez), por ejemplo de JSON . [6]

Los iteradores son una abstracción muy general y se pueden utilizar para cualquier tipo de procesamiento de información secuencial (o procesamiento mixto secuencial/de acceso aleatorio) y no necesitan involucrar ninguna operación de E/S. Esto facilita la reutilización de un iterador para trabajar en un conjunto de datos en memoria en lugar de datos que fluyen desde la red.

Historia

En cierto sentido, un predecesor lejano de la noción de un enumerador que introduce datos en una cadena de uno o más iteradores fue el concepto de canalización en los sistemas operativos. Sin embargo, a diferencia de una canalización típica, los iteradores no son procesos separados (y por lo tanto no tienen la sobrecarga de IPC ), o incluso hilos separados, aunque pueden realizar el trabajo de manera similar a una cadena de hilos de trabajo que se envían mensajes entre sí. Esto significa que los iteradores son más livianos que los procesos o hilos; a diferencia de las situaciones con procesos o hilos separados, no se necesitan pilas adicionales.

Los iteradores y enumeradores fueron inventados por Oleg Kiselyov para su uso en Haskell. [5] Posteriormente, se introdujeron en Scalaz (en la versión 5.0; los enumeradores estaban ausentes y se introdujeron en Scalaz 7) y en Play Framework 2.0.

Semántica formal

Los iterados se han modelado formalmente como mónadas libres , lo que permite validar leyes ecuacionales y emplearlas para optimizar programas que utilizan iterados. [5]

Alternativas

  • Se pueden utilizar iteradores en lugar de iterados en Scala, pero son imperativos , por lo que no son una solución puramente funcional .
  • En Haskell, se han desarrollado dos abstracciones alternativas conocidas como conductos y tuberías (estos conductos no son conductos a nivel de sistema operativo, por lo que, al igual que los iteradores, no requieren el uso de llamadas al sistema ). Los conductos, en particular, están asociados con bibliotecas de primitivos y combinadores sustancialmente más ricas que los iteradores; existen adaptadores de conductos para funcionalidades incrementales como el análisis de HTML, XML, el análisis generalizado, la realización de solicitudes HTTP y el procesamiento de las respuestas, lo que hace que los conductos sean más adecuados que los iteradores para el desarrollo de software industrial en Haskell, de manera inmediata.
  • También existe una abstracción de alto nivel denominada máquinas. En Scala existe un paquete llamado FS2: Functional Streams for Scala, cuyo origen se remonta a las máquinas a través de varios puertos, cambios de nombre y refactorizaciones.
  • En Haskell, existe el paquete safe-lazy-io, que ofrece una solución más sencilla para algunos de los mismos problemas, que básicamente implica ser "lo suficientemente estricto" para extraer todos los datos que se requieren, o que podrían requerirse, a través de una secuencia de comandos que se encarga de limpiar los recursos al finalizar.

Referencias

  1. ^ "Manejo reactivo de flujos de datos". Documentación de Play Framework . Consultado el 29 de junio de 2013 .
  2. ^ "Resultados de búsqueda en Github: Iteratee en FSharpx". GitHub .
  3. ^ "Teoría y práctica de Java: el debate sobre las excepciones". IBM developerWorks . Consultado el 17 de mayo de 2014 .
  4. ^ "Enumerados". Documentación del marco de trabajo de Play . Consultado el 29 de junio de 2013 .
  5. ^ abc Kiselyov, O. (2012). "Iterates". Programación funcional y lógica . Apuntes de clase en informática. Vol. 7294. págs. 166-181. doi :10.1007/978-3-642-29822-6_15. ISBN 978-3-642-29821-9.
  6. ^ James Roper (10 de diciembre de 2012). "Json.scala". play-iteratees-extras . Consultado el 29 de junio de 2013 .

Lectura adicional

  • John W. Lato (12 de mayo de 2010). "Iteratee: Teaching an Old Fold New Tricks" (Iteratee: enseñarle nuevos trucos a un viejo Fold). Número 16 de The Monad Reader . Consultado el 29 de junio de 2013 .Esto se relaciona con Haskell.
  • Tutoriales de Scala
    • Jugar 2.0
      • Entendiendo Play 2 iteraciones para humanos normales
      • Iteradores para programadores imperativos
    • Escalada
      • Tutorial de Scalaz: E/S basada en enumeración con iteradores
  • Tutoriales de Haskell
    • Notas de la conferencia de Stanford
  • Más información
    • Página de iteradores y enumeradores de Oleg Kiselyov
Obtenido de "https://es.wikipedia.org/w/index.php?title=Iteratee&oldid=1160620675"