Powered by AppSignal & Oban Pro

Flujos o Streams

getting_started/streams.livemd

Flujos o Streams

Introducción

Como alternativa a Enum, Elixir provee el módulo Stream, el cual soporta evaluación perezosa.

1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(odd?) |> Enum.sum()

Como ya mencionaba, los Streams son perezosos, y también una manera de componer numerables.

En el ejemplo anterior, 1..100_000 |> Stream.map(&(&1 * 3)) retorna un tipo de dato, un flujo, que representa el cálculo triplica el valor de cada elemento del rango 1..100_000

1..100_000 |> Stream.map(&(&1 * 3))

Además, podemos decir que son componibles porque podemos juntar secuencias de operaciones sobre flujos o streams.

1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(odd?)

En lugar de generar listas intermedias, los flujos construyen una serie de cálculos que en realidad se invocan solo cuando pasamos el flujo al módulo Enum. Los flujos son útiles cuando trabajamos con largas, posiblemente infinita, colecciones de datos.

Muchas funciones en el módulo Stream aceptan cualquier numerable como argumento y retornan un flujo como resultado. También provee funciones para crear flujos. Por ejemplo, Stream.cycle/1 puede ser usada para crear un flujo que genera un ciclo infinito del numerable dado. Ten cuidado y evita llamar funciones como Enum.map/2 sobre dicho flujo, de lo contrario generarás un ciclo infinito.

stream = Stream.cycle([1, 2, 3])
Enum.take(stream, 10)

Por otro lado, Stream.unfold/2 puede usarse para generar valores a partir de un valor inicial dado:

stream = Stream.unfold("hełło", &String.next_codepoint/1)
Enum.take(stream, 3)

Otra función interesante es Stream.resource/3, la cual puedes usar para envolver recursos, garantizando que dichos recursos se abran justo antes de numeración y se cierren al final, incluso en caso de fallas. Por ejemlo, File.stream!/1 se basa en Stream.resource/3 para manejar ficheros como flujos.

stream = File.stream!("path/to/file")
Enum.take(stream, 10)

El ejemplo anterior obtendrá las primeras 10 líneas del fichero que seleccionaste. Esto significa que las secuencias pueden ser muy útiles para manejar archivos grandes o incluso recursos lentos como los recursos de red.

La cantidad de funcionalidades en los módulos Enum y Stream puede ser abrumador al principio, pero con la práctica te irás familiarizando con ellos. En particular, te recomiendo concentrarte primero en el módulo Enum y solo después puedes avanzar a Stream en aquellos casos donde veas conveniente la evaluación perezosa, ya sea para manejar recursos lentos o colecciones grandes, posiblemente infinitas.

En la próxima clase, veremos una característica central de Elixir, los Procesos, los cuales nos permite escribir programas concurrentes, paralelos y distribuidos de una manera fácil y comprensible.