tee
Ejecuta varios folds sobre una pasada de la fuente: una iteración, nada almacenado.
Lección
Dos preguntas sobre los mismos datos suelen costar dos pasadas:
readings.fold(...) para el total y luego
readings.reduce(...) para el pico. Eso está bien para una
List, y está mal para cualquier otra cosa: un generador
sync*, una página de red o una fuente que cuenta cuántas
veces se ejecutó se recorrerán dos veces. tee hace las dos
preguntas a la vez: cada elemento hace avanzar el total y el pico
antes de tirar del siguiente, así que la fuente se recorre exactamente una
vez.
Un lector se da como un fold: un record con
seed (dónde empieza) y step (cómo lo hace
avanzar un elemento). Esa forma es lo que hace gratis la pasada única.
Como los dos lectores se mueven juntos, elemento a elemento, nunca hay un
valor que uno haya visto y el otro no, así que no hay nada que recordar:
tee sobre un millón de elementos sostiene dos acumuladores,
no un millón de valores. Los dos acumuladores son totalmente
independientes y no tienen por qué compartir tipo. tee3 admite tres.
La restricción es el precio de eso. tee alimenta folds, no
pipelines: los lectores no pueden avanzar a su propio ritmo, tomar
cantidades distintas ni pararse antes por su cuenta. Cuando de verdad
necesitas dos lectores independientes, echa mano de
fork y acepta el búfer compartido que
mantiene para que un cursor rezagado pueda alcanzar al otro. Regla
práctica: si los dos lectores consumen la fuente entera y la reducen a un
valor, tee; si alguno es un pipeline por derecho propio,
fork.
De dónde viene el nombre
tee no es una abreviatura: es la letra T,
tomada de la pieza en T de la fontanería. Un empalme en T divide una
tubería en dos, así que lo que entra por un lado sale por dos a la vez.
Unix se quedó con la imagen para su orden tee, que lee la
entrada estándar y la envía a la salida estándar y a un fichero
al mismo tiempo:
input
│
▼
┌───┴───┐
│ tee │
└───┬───┘
┌────┴────┐
▼ ▼
stdout file
El itertools.tee() de Python toma prestada la misma imagen,
dividiendo un iterable en varios iteradores independientes. Conviene
saberlo, porque esa es la parte que FxDart llama
fork, no tee:
fork te da cursores independientes, como el de Python. El
tee de FxDart ramifica el consumo en su lugar: una
pasada, varios folds leyéndola al unísono. La misma imagen en forma de T,
dividida un nivel más aguas abajo.
Demo 1 · Total y pico con una sola lectura
sensor() incrementa reads por cada valor que
produce. Dos pasadas separadas dejarían reads en 12;
tee lo deja en 6:
Demo 2 · Acumuladores independientes, tee3, y sobre una cadena
Los dos folds llevan tipos que no tienen nada que ver: un recuento de
caracteres int junto a un String con el ganador
provisional. tee3 añade un tercer fold y, sobre una cadena
fx, los folds ven lo que produce la cadena, no la
fuente original:
Pruébalo tú
Ejercicio: ahora mismo sensor() se recorre dos veces, así que
reads imprime 6. Sustituye las dos pasadas por un solo
tee — sumando en un fold y contando en el otro — para que
reads imprima 3.