1

There's a particular style of computation that comes up now and then, that I don't know how to nicely translate into map/filter/reduce. I'm wondering if there's a standard functional-style function to do it, or what a good name is.

Basically, you find yourself following directions while yielding things at each stop. Here's a C# function showing the general idea:

public static IEnumerable<TOut> streamAggregateMap<TDif, TAcc, TOut>(
        this IEnumerable<TDif> difs,
        TAcc seed,
        Func<TAcc, TDif, TAcc> accumulator,
        Func<TAcc, TDif, TOut> selector) {
    var acc = seed;
    foreach (var dif in difs) {
        yield return selector(acc, dif);
        acc = accumulator(acc, dif);
    }
}

You can use it to trace down a tree while yielding values:

var vals = goLeftDirections.streamAggregateMap(
    root,
    (node, goLeft) => goLeft ? node.left : node.right,
    (node, goLeft) => node.value);

Or tell you the prime factorizations of numbers you get by iteratively multiplying by 2 and adding some offsets, except the factorizations are of numbers offset without multiplying by 2:

var factorizations = offsets.streamAggregateMap(
    1,
    (total, offset) => total * 2 + offset,
    (total, offset) => (total + offset).Factorize());

Is there a standard name for this "accumulate while yielding related values" functionality? A standard function to use?

Craig Gidney
  • 17,763
  • 5
  • 68
  • 136

1 Answers1

0

MoreLINQ calls this operation Scan.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • How would you implement my factorization example in terms of scan? – Craig Gidney Jul 25 '14 at 19:57
  • @Strilanc `offsets.Scan(new{total = 1, offset = 0}, (acc, offset) => new{total = total * 2 + offset, offset}).Select(acc => (acc.total + offset).Factorize())`. `Scan` doesn't apply separate selectors for yielding and accumulating, so one would sensibly implement `Scan` in terms of your implementation, but the conceptual idea of aggregating values and yielding as you go is what both operations are performing. The designers of MoreLinq apparently just didn't imagine why anyone would want to yield a value other than the new total. Having said that, as I showed, you can use `Scan` + `Select`. – Servy Jul 25 '14 at 20:10