C# 函数式编程:聚合直到函数

C# 函数式编程:聚合直到函数,c#,functional-programming,C#,Functional Programming,所以这里有一个有趣的问题。我试图用一种功能性的方法来解决一些非常简单的问题。目标是获取一个序列并将其foldl/减少为单个值,但是我希望在累积值满足给定条件后尽早停止并退出。您可能会说我想定义IEnumerable.AggregateUntil。以下是我将如何以命令式的方式编写它: public static TAccumulate AggregateUntil<TSource, TAccumulate>( this IEnumerable<TSource> so

所以这里有一个有趣的问题。我试图用一种功能性的方法来解决一些非常简单的问题。目标是获取一个序列并将其foldl/减少为单个值,但是我希望在累积值满足给定条件后尽早停止并退出。您可能会说我想定义IEnumerable.AggregateUntil。以下是我将如何以命令式的方式编写它:

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);