Pursuant to a different issue, I was using Aggregate and needed to exit early if a certain intermediate value occurred, which ended up involving a lot of ternary operators (or a lambda body with if) to bypass the aggregation when that value was reached, so I wrote some new extensions to exit early:
public static T AggregateWhile<T>(this IEnumerable<T> src, Func<T, T, T> accumFn, Predicate<T> whileFn) {
using (var e = src.GetEnumerator()) {
if (!e.MoveNext())
throw new Exception("At least one element required by AggregateWhile");
var ans = e.Current;
while (whileFn(ans) && e.MoveNext())
ans = accumFn(ans, e.Current);
return ans;
}
}
public static TAccum AggregateWhile<TAccum, T>(this IEnumerable<T> src, TAccum seed, Func<TAccum, T, TAccum> accumFn, Predicate<TAccum> whileFn) {
using (var e = src.GetEnumerator()) {
if (!e.MoveNext())
throw new Exception("At least one element required by AggregateWhile");
var ans = accumFn(seed, e.Current);
while (whileFn(ans) && e.MoveNext())
ans = accumFn(ans, e.Current);
return ans;
}
}
For your example, you could use it as follows:
var result = new List<bool> {
false,
foo(),
bar()
}.AggregateWhile(true, (acc, x) => acc && x, acc => acc);
Of course, foo() and bar() will be executed because a List<bool> doesn't defer execution, so only the loop time is saved. But if you had a sequence that does:
var result = new List<Func<bool>> { () => false, () => foo(), () => bar() }
.AggregateWhile(true, (acc, f) => acc && f(), acc => acc);
that would skip executing foo and bar.
The obvious counterpart AggregateUntils are left as an exercise :)