LINQ聚合算法解释

这可能听起来很蹩脚,但我一直无法找到对Aggregate很好解释。

良好意味着简短,描述性,全面,小而明确的例子。


Aggregate最容易理解的定义是它考虑到之前的操作,对列表中的每个元素执行操作。 也就是说,它对第一个和第二个元素执行操作并将结果向前传递。 然后它对前一个结果和第三个元素进行操作并继续。 等等

示例1.总结数字

var nums = new[]{1,2,3,4};
var sum = nums.Aggregate( (a,b) => a + b);
Console.WriteLine(sum); // output: 10 (1+2+3+4)

这增加了12使3 。 然后将3 (前一个结果)和3 (序列中的下一个元素)添加为6 。 然后加6410

示例2.从一个字符串数组中创建一个csv

var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate( (a,b) => a + ',' + b);
Console.WriteLine(csv); // Output a,b,c,d

这种工作方式非常相似。 连接a逗号和b来创建a,b 。 然后连接a,b和逗号以及c来创建a,b,c 。 等等。

示例3.使用种子来乘数

为了完整起见, Aggregate的重载需要一个种子值。

var multipliers = new []{10,20,30,40};
var multiplied = multipliers.Aggregate(5, (a,b) => a * b);
Console.WriteLine(multiplied); //Output 1200000 ((((5*10)*20)*30)*40)

就像上面的例子一样,它的值从5开始,然后乘以序列10的第一个元素,结果为50 。 该结果继续前进,并乘以序列20的下一个数字,得出1000的结果。 这继续通过序列的剩余2个元素。

现场示例:http://rextester.com/ZXZ64749
文档:http://msdn.microsoft.com/en-us/library/bb548651.aspx


附录

上面的示例2使用字符串连接来创建用逗号分隔的值列表。 这是解释使用的一个简单的方法Aggregate这是这个答案的意图。 但是,如果使用这种技术来实际创建大量逗号分隔的数据,那么使用StringBuilder会更合适,并且这与使用种子重载启动StringBuilder Aggregate完全兼容。

var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate(new StringBuilder(), (a,b) => {
    if(a.Length>0)
        a.Append(",");
    a.Append(b);
    return a;
});
Console.WriteLine(csv);

更新示例:http://rextester.com/YZCVXV6464


这部分取决于您所谈论的超载,但基本思路是:

  • 以种子作为“当前值”开始
  • 迭代序列。 对于序列中的每个值:
  • 应用用户指定的函数将(currentValue, sequenceValue)转换为(nextValue)
  • 设置currentValue = nextValue
  • 返回最终的currentValue
  • 您可能会发现我的Edulinq系列中的Aggregate post很有用 - 它包含更详细的描述(包括各种重载)和实现。

    一个简单的例子是使用Aggregate作为Count的替代方法:

    // 0 is the seed, and for each item, we effectively increment the current value.
    // In this case we can ignore "item" itself.
    int count = sequence.Aggregate(0, (current, item) => current + 1);
    

    或者,也许总结字符串序列中所有长度的字符串:

    int total = sequence.Aggregate(0, (current, item) => current + item.Length);
    

    就我个人而言,我很少发现Aggregate有用 - “量身定制”的聚合方法通常对我来说足够好。


    超短集合像Haskell / ML / F#中的折叠一样工作。

    稍微长一些 .Max(),.Min(),.Sum(),.Average()全部遍历序列中的元素并使用各自的聚合函数进行聚合。 .Aggregate()是通用的聚合器,它允许开发者指定开始状态(又名种子)和聚合函数。

    我知道你问了一个简短的解释,但我认为其他人给出了几个简短的答案我认为你可能会对一个稍长一点的感兴趣

    使用代码的长版本一种方法可以说明如何使用foreach和一次使用.Aggregate来实现Sample Standard Deviation。 注意:我没有在这里优先考虑性能,所以我不必要地在集合中多次迭代

    首先用于创建二次距离和的辅助函数:

    static double SumOfQuadraticDistance (double average, int value, double state)
    {
        var diff = (value - average);
        return state + diff * diff;
    }
    

    然后使用ForEach进行样本标准偏差:

    static double SampleStandardDeviation_ForEach (
        this IEnumerable<int> ints)
    {
        var length = ints.Count ();
        if (length < 2)
        {
            return 0.0;
        }
    
        const double seed = 0.0;
        var average = ints.Average ();
    
        var state = seed;
        foreach (var value in ints)
        {
            state = SumOfQuadraticDistance (average, value, state);
        }
        var sumOfQuadraticDistance = state;
    
        return Math.Sqrt (sumOfQuadraticDistance / (length - 1));
    }
    

    然后一次使用。聚集:

    static double SampleStandardDeviation_Aggregate (
        this IEnumerable<int> ints)
    {
        var length = ints.Count ();
        if (length < 2)
        {
            return 0.0;
        }
    
        const double seed = 0.0;
        var average = ints.Average ();
    
        var sumOfQuadraticDistance = ints
            .Aggregate (
                seed,
                (state, value) => SumOfQuadraticDistance (average, value, state)
                );
    
        return Math.Sqrt (sumOfQuadraticDistance / (length - 1));
    }
    

    请注意,除了计算sumOfQuadraticDistance的方式外,这些函数是相同的:

    var state = seed;
    foreach (var value in ints)
    {
        state = SumOfQuadraticDistance (average, value, state);
    }
    var sumOfQuadraticDistance = state;
    

    与:

    var sumOfQuadraticDistance = ints
        .Aggregate (
            seed,
            (state, value) => SumOfQuadraticDistance (average, value, state)
            );
    

    那么.Aggregate做的是封装这个聚合器模式,我期望.Aggregate的实现看起来像这样:

    public static TAggregate Aggregate<TAggregate, TValue> (
        this IEnumerable<TValue> values,
        TAggregate seed,
        Func<TAggregate, TValue, TAggregate> aggregator
        )
    {
        var state = seed;
    
        foreach (var value in values)
        {
            state = aggregator (state, value);
        }
    
        return state;
    }
    

    使用标准偏差函数看起来像这样:

    var ints = new[] {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
    var average = ints.Average ();
    var sampleStandardDeviation = ints.SampleStandardDeviation_Aggregate ();
    var sampleStandardDeviation2 = ints.SampleStandardDeviation_ForEach ();
    
    Console.WriteLine (average);
    Console.WriteLine (sampleStandardDeviation);
    Console.WriteLine (sampleStandardDeviation2);
    

    恕我直言

    那么。汇总帮助可读性? 一般来说,我喜欢LINQ,因为我认为.Where,.Select,.OrderBy等等极大地提高了可读性(如果避免内联分层.Select)。 为了完整性原因,Aggregate必须在Linq中,但是我个人并不这么认为.Aggregate增加了可读性,而不是写得很好的foreach。

    链接地址: http://www.djcxy.com/p/31911.html

    上一篇: LINQ Aggregate algorithm explained

    下一篇: linq group by: good syntax but weird output