Esta página ainda não foi traduzida, por isso é exibida em inglês. Ajude a traduzir

tee

Runs several folds over one pass of a source — one iteration, nothing buffered.

typedef Fold<A, R> = ({R seed, R Function(R acc, A a) step}); (R1, R2) tee<A, R1, R2>(Iterable<A> iterable, Fold<A, R1> first, Fold<A, R2> second) (R1, R2, R3) tee3<A, R1, R2, R3>(Iterable<A> iterable, Fold<A, R1> first, Fold<A, R2> second, Fold<A, R3> third) Future<(R1, R2)> teeAsync<A, R1, R2>(FxAsyncIterable<A> iterable, AsyncFold<A, R1> first, AsyncFold<A, R2> second)

Lecture

Two questions about the same data usually cost two passes: readings.fold(...) for the total, then readings.reduce(...) for the peak. That is fine for a List, and wrong for anything else — a sync* generator, a network page, a source that counts how often it ran will all be walked twice. tee asks both questions at once: each element advances the total and the peak before the next one is pulled, so the source is iterated exactly once.

A reader is given as a fold — a record of seed (where it starts) and step (how one element advances it). That shape is what makes the single pass free. Because both readers move together, element by element, there is never a value that one has seen and the other has not, so there is nothing to remember: tee over a million elements holds two accumulators, not a million values. The two accumulators are entirely independent and need not share a type. tee3 takes three.

The constraint is the price of that. tee feeds folds, not pipelines — the readers cannot advance at their own pace, take different amounts, or stop early independently. When you genuinely need two independent readers, reach for fork instead, and accept the shared buffer it keeps so a lagging cursor can catch up. Rule of thumb: if both readers consume the whole source and reduce it to a value, tee; if either one is a pipeline in its own right, fork.

Where the name comes from

tee is not an abbreviation — it is the letter T, borrowed from the T-splitter used in plumbing. A T-fitting branches one pipe into two, so what flows in one way leaves two ways at once. Unix took the image for its tee command, which reads standard input and sends it to standard output and a file at the same time:

       input
        │
        ▼
    ┌───┴───┐
    │  tee  │
    └───┬───┘
   ┌────┴────┐
   ▼         ▼
  stdout    file

Python's itertools.tee() borrows the same picture, splitting one iterable into several independent iterators. Worth knowing, because that is the part FxDart spells fork, not tee: fork gives you independent cursors, the way Python's does. FxDart's tee branches the consumption instead — one pass, several folds reading it in step. Same T-shaped picture, split one level further downstream.

Demo 1 · Total and peak from one read

sensor() increments reads for every value it produces. Two separate passes would leave reads at 12; tee leaves it at 6:

Demo 2 · Independent accumulators, tee3, and on a chain

The two folds carry unrelated types — an int character count beside a String running winner. tee3 adds a third fold, and on an fx chain the folds see what the chain produces, not the original source:

Try it yourself

Exercise: sensor() is currently walked twice, so reads prints 6. Replace the two passes with a single tee — summing in one fold and counting in the other — so that reads prints 3.

Related: fork — independent readers, at the cost of a buffer · reduce — a single fold · groupBy — many accumulators keyed by value