Draining the Leaf, One Steep at a Time
Gongfu brewing means you don’t make one cup of tea, you make eight. A dense little pinch of oolong, a tiny pot, and a sequence of steeps that get progressively shorter or longer while you chase the same leaves from bright and grassy down to thin and mineral. Somewhere around the fourth pour it hit me that I already know this shape. It’s a difference equation. Each steep pulls out a fraction of whatever solubles are still trapped in the leaf, and next time there’s less to pull.
Write the state as remaining, the fraction of extractables still in the leaf. Each steep grabs a share f and the rest carries forward: remaining[n+1] = remaining[n] - remaining[n]*f. That single line is the whole model. The 1980s numerical-methods crowd would have called it a discrete recurrence and simulated it with a loop, one term feeding the next.
Pascal does it exactly that way — state, mutate, print, repeat:
program Steeps;
var
remaining, f, cup: real;
s: integer;
begin
remaining := 1.0; { solubles still in the leaf }
f := 0.45; { fraction pulled per steep }
for s := 1 to 8 do
begin
cup := remaining * f;
writeln('steep ', s, ': ', cup:6:3);
remaining := remaining - cup
end
end.
steep 1: 0.450
steep 2: 0.248
steep 3: 0.136
steep 4: 0.075
...
R refuses to think one step at a time. Because the recurrence has a closed form — remaining after n steeps is just (1-f)^n — you can hand R the whole sequence at once and let it compute every cup in parallel:
f <- 0.45
steeps <- 1:8
cup <- (1 - f)^(steeps - 1) * f
names(cup) <- paste0("s", steeps)
round(cup / max(cup), 2)
s1 s2 s3 s4 s5 s6 s7 s8
1.00 0.55 0.30 0.17 0.09 0.05 0.03 0.02
Same numbers, two temperaments: Pascal marches through the states it can’t skip, R jumps straight to the algebra and vectorizes. The tidy geometric decay is a lie about the first cup, mind you — real leaves need a steep or two to unfurl, so the true curve ramps up before it falls. My tongue insists the third pour was the best one, and no exponent I picked will admit that.