Pipelining can be a useful operation when you need to break up code into several steps, perhaps for readability. Typically this is done to avoid a huge mess of nested functions: f(g(h(i(j(k(l(x)))))). Without pipelining you typically need to assign the various steps to local variables. You can get pipelining in C# by extending object with this extension method:
public static TResult Pipe<T, TResult>(this T obj, Func<T, TResult> f)
{
return f(obj);
}
Example calculating standard deviation with and without pipelining:
List<double> values = new List<double>() { 1, 7, 8, 9, 10, 100, 1000, 1001, 100000 };
double average = values.Average();
double totalVariance = 0;
foreach (double value in values)
{
totalVariance += Math.Pow(value - average, 2);
}
//OR you could do this:
//totalVariance = values.Aggregate(0.0, (variance, val) => variance + Math.Pow(val - average, 2));
double stdDeviation = Math.Sqrt(totalVariance / values.Count);
//Now with pipe
stdDeviation = values
.Pipe(v => v.Average())
.Pipe(avg => values.Aggregate(0.0, (variance, val) => variance + Math.Pow(val - avg, 2)))
.Pipe(totVariance => Math.Sqrt(totalVariance / values.Count));
Even int gets Pipe():
(2) .Pipe(i => Math.Pow(i, 42)) .Pipe(i42 => Math.Sin(i42));
The benefit, as far as I’m concerned, is avoiding uncessary mutable variables in the function scope (or at least from leaking out to where they don’t need to be).
If delegate-invoke didnt suck so much performance-wise, this would be nice.
You’ve reinvented map (http://en.wikipedia.org/wiki/Fold_(higher-order_function))
No. Map typically deals with a sequence of things. In LINQ the equivalent is IEnumerable.Select. Pipe does not require a sequence or IEnumerable.
@dm3: not quite – the LINQ equivalent of map is Select, and the LINQ equivalent of fold is Aggregate which he has used
the LINQ version of this is:
var std = Math.Sqrt((from v in values
let avg = values.Average()
select Math.Pow(v – avg, 2)).Average());
which benefits a lot from LINQ having Average built in.