What I really want is a general API for composing folds by making a single pass in the iterator.
Because, there is already Iterator::max
and Iterator::min
(and Iterator::fold
in general), but those don't compose and then itertools has to define Itertools::minmax
.
Maybe the only way to compose folds is to build a "fold" that is independent from the iterator, and then have some API that applies all folds (and returns a tuple). But then this fold is just a closure (or a struct that wraps a closure).
One issue is that folds usually get ownership but for running multiple folds in the same pass we should borrow (either & or &mut is ok).
But it would be something like
let x = build_fold(.., |acc, x| ..);
let y = build_fold(.., |acc, x| ..);
// returns a tuple (s, q) by running making a single pass on iterator
x.compose(y).fold(iterator)
Perhaps another way is to have a "composable fold" that is tied to an iterator, and a way to process them in lockstep that guarantees that, if those folds are processing the same iterator, they should access the iterator just one time per element iterated (but if they are from different iterators, they will access different elements). Something like
let x = a.iter().composable_fold(.., |acc, x| ..); // or a.iter().fold_compose(..)
let y = a.iter().composable_fold(.., |acc, x| ..);
let z = b.iter().composable_fold(.., |acc, x| ..);
// makes a single pass zipping a and b, returning a triple (p, q, r) of folded elements
x.compose(y).compose(z).fold()
Now, how does the API could possibly know that x and y are folding over a single iterator, and z is iterating over another iterator? Maybe it isn't possible to detect this.