stringtranslate.com

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 enumeratee es una abstracción conveniente para transformar la salida de un enumerador o iterador y enviar esa salida a un iterador. Por ejemplo, un enumeratee "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

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

Enlaces externos