260: Stateful Accumulation with scan()
Functional Programming
Tutorial
The Problem
Computing running totals, prefix sums, or any sequence where each value depends on all previous values is a common need in financial systems, signal processing, and data streaming. A plain fold() produces only the final accumulated result. The scan() adapter solves this by emitting each intermediate accumulated state as an element — turning a fold into a stream of partial results.
🎯 Learning Outcomes
scan() as fold() that yields each intermediate state rather than only the final valuescan() closureNone from a scan() closure to terminate the sequence earlyCode Example
#![allow(clippy::all)]
//! 260. Stateful accumulation with scan()
//!
//! `scan()` is like `fold` but emits each intermediate state as an iterator element.
#[cfg(test)]
mod tests {
#[test]
fn test_scan_running_sum() {
let result: Vec<i32> = [1, 2, 3, 4, 5]
.iter()
.scan(0i32, |s, &x| {
*s += x;
Some(*s)
})
.collect();
assert_eq!(result, vec![1, 3, 6, 10, 15]);
}
#[test]
fn test_scan_early_stop() {
let result: Vec<i32> = [1, 2, 3, 4, 5]
.iter()
.scan(0i32, |s, &x| {
*s += x;
if *s > 6 {
None
} else {
Some(*s)
}
})
.collect();
assert_eq!(result, vec![1, 3, 6]);
}
#[test]
fn test_scan_product() {
let result: Vec<i32> = [1, 2, 3, 4]
.iter()
.scan(1i32, |s, &x| {
*s *= x;
Some(*s)
})
.collect();
assert_eq!(result, vec![1, 2, 6, 24]);
}
}Key Differences
scan() emits every intermediate state; OCaml's equivalent must explicitly accumulate intermediates into a list.scan() can return None to stop the sequence; a fold-based OCaml approach requires extra logic.&mut state, making mutation explicit; OCaml uses functional update via return value.np.cumsum(), in databases as window functions, and in streaming analytics as running aggregates.OCaml Approach
OCaml's List.scan_left (introduced in recent versions) directly mirrors this pattern, or one can build it with List.fold_left accumulating into a reversed list of intermediates:
(* Manual scan: fold collecting intermediates *)
let scan_left f init xs =
List.rev (snd (List.fold_left (fun (acc, lst) x ->
let acc' = f acc x in (acc', acc' :: lst)
) (init, []) xs))
OCaml's Seq module makes this natural for lazy streams.
Full Source
#![allow(clippy::all)]
//! 260. Stateful accumulation with scan()
//!
//! `scan()` is like `fold` but emits each intermediate state as an iterator element.
#[cfg(test)]
mod tests {
#[test]
fn test_scan_running_sum() {
let result: Vec<i32> = [1, 2, 3, 4, 5]
.iter()
.scan(0i32, |s, &x| {
*s += x;
Some(*s)
})
.collect();
assert_eq!(result, vec![1, 3, 6, 10, 15]);
}
#[test]
fn test_scan_early_stop() {
let result: Vec<i32> = [1, 2, 3, 4, 5]
.iter()
.scan(0i32, |s, &x| {
*s += x;
if *s > 6 {
None
} else {
Some(*s)
}
})
.collect();
assert_eq!(result, vec![1, 3, 6]);
}
#[test]
fn test_scan_product() {
let result: Vec<i32> = [1, 2, 3, 4]
.iter()
.scan(1i32, |s, &x| {
*s *= x;
Some(*s)
})
.collect();
assert_eq!(result, vec![1, 2, 6, 24]);
}
}
✓ Tests
Rust test suite
#[cfg(test)]
mod tests {
#[test]
fn test_scan_running_sum() {
let result: Vec<i32> = [1, 2, 3, 4, 5]
.iter()
.scan(0i32, |s, &x| {
*s += x;
Some(*s)
})
.collect();
assert_eq!(result, vec![1, 3, 6, 10, 15]);
}
#[test]
fn test_scan_early_stop() {
let result: Vec<i32> = [1, 2, 3, 4, 5]
.iter()
.scan(0i32, |s, &x| {
*s += x;
if *s > 6 {
None
} else {
Some(*s)
}
})
.collect();
assert_eq!(result, vec![1, 3, 6]);
}
#[test]
fn test_scan_product() {
let result: Vec<i32> = [1, 2, 3, 4]
.iter()
.scan(1i32, |s, &x| {
*s *= x;
Some(*s)
})
.collect();
assert_eq!(result, vec![1, 2, 6, 24]);
}
}
Exercises
scan() — the product of all elements up to and including each position.scan() to implement a running maximum: each output is the maximum seen so far.scan() to emit the running account balance and terminate when it goes negative.