C# 函数式编程:聚合直到函数
所以这里有一个有趣的问题。我试图用一种功能性的方法来解决一些非常简单的问题。目标是获取一个序列并将其foldl/减少为单个值,但是我希望在累积值满足给定条件后尽早停止并退出。您可能会说我想定义IEnumerable.AggregateUntil。以下是我将如何以命令式的方式编写它:C# 函数式编程:聚合直到函数,c#,functional-programming,C#,Functional Programming,所以这里有一个有趣的问题。我试图用一种功能性的方法来解决一些非常简单的问题。目标是获取一个序列并将其foldl/减少为单个值,但是我希望在累积值满足给定条件后尽早停止并退出。您可能会说我想定义IEnumerable.AggregateUntil。以下是我将如何以命令式的方式编写它: public static TAccumulate AggregateUntil<TSource, TAccumulate>( this IEnumerable<TSource> so
public static TAccumulate AggregateUntil<TSource, TAccumulate>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TSource, TAccumulate, TAccumulate> accumulate,
Func<TAccumulate, bool> until)
{
var result = seed;
foreach (var s in source)
{
result = accumulate(s, result);
if (until(result))
{
break;
}
}
return result;
}
编辑2:
好的,我已经实现了我的目标函数,但我还没有想到如果不重新实现基本上所有的foldl/reduce/aggregate逻辑+until条件,如何实现它。如果我不知道如何按原样在聚合中重用逻辑,我觉得我缺少了FP可组合性的一个基本技巧:
您可以这样做:
public static TAccumulate AggregateUntil<TSource, TAccumulate>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TSource, TAccumulate, TAccumulate> accumulate,
Func<TAccumulate, bool> until)
{
return source.Select(s => seed = accumulate(s, seed))
.SkipWhile(s => !until(s))
.First();
}
选择不太好,因为我们保持状态。但是我认为,如果不重新编写聚合以打破某个条件或多次聚合列表,就不可能做到这一点。最好使用命令式技术,因为实现这一点的功能方法是使用递归,并且可以在C中使用递归轻松地炸毁堆栈,它的效率也要低得多——递归调用有一个开销,您需要多次从枚举中获取第一项 但是,如果您想知道如何使用,请将其作为IEnumerable的扩展方法:
public static TAccumulate AggregateUntil<TSource, TAccumulate>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TSource, bool> predicate,
Func<TSource, TAccumulate, TAccumulate> accumulate)
{
return source.Any()
? predicate(source.First())
? source.Skip(1).AggregateUntil(accumulate(source.First(), seed), predicate, accumulate)
: seed
: seed;
}
如果您使用my library,则可以使用C接近此样式:
S FoldUntil<S,T>(IEnumerable<T> list, S state, Func<S, T, S> folder, Func<T, bool> pred) =>
match(list,
() => state,
(x, xs) => pred(x)
? FoldUntil(xs, folder(state, x), folder, pred)
: state);
在基于某些条件尝试退出折叠/累积时,我得到的一般建议是使用Recycrosion。如果直到所有s都返回false,这不应该失败吗?@很快,是的,你说得对-很好。会在我有机会时更新
public static TAccumulate AggregateUntil<TSource, TAccumulate>(
this IEnumerable<TSource> source,
TAccumulate seed,
Func<TSource, bool> predicate,
Func<TSource, TAccumulate, TAccumulate> accumulate)
{
return source.Any()
? predicate(source.First())
? source.Skip(1).AggregateUntil(accumulate(source.First(), seed), predicate, accumulate)
: seed
: seed;
}
let rec foldUntil state folder pred = function
| [] -> state
| x :: xs -> if pred x
then foldUntil (folder state x) folder pred xs
else state
S FoldUntil<S,T>(IEnumerable<T> list, S state, Func<S, T, S> folder, Func<T, bool> pred) =>
match(list,
() => state,
(x, xs) => pred(x)
? FoldUntil(xs, folder(state, x), folder, pred)
: state);