LINQ聚合算法解释

发布于 2024-11-30 08:16:14 字数 90 浏览 3 评论 0 原文

这可能听起来很蹩脚,但我还没有找到对Aggregate真正好的解释。

好的意味着简短、描述性、全面,并有一个小而清晰的例子。

This might sound lame, but I have not been able to find a really good explanation of Aggregate.

Good means short, descriptive, comprehensive with a small and clear example.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

剧终人散尽 2024-12-07 08:16:15

聚合主要用于对数据进行分组或求和。

根据 MSDN
“聚合函数对序列应用累加器函数。”

示例 1:将数组中的所有数字相加。

int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate((total, nextValue) => total + nextValue);

*重要提示:默认情况下,初始聚合值是集合序列中的第 1 个元素。
即:total变量初始值默认为1。

变量解释

total:它将保存func返回的总和值(聚合值)。

nextValue:它是数组序列中的下一个值。然后将该值添加到聚合值(即总计)中。

示例 2:添加数组中的所有项目。还设置初始累加器值从 10 开始相加。

int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);

参数说明:

第一个参数是初始值(起始值,即种子值),它将用于开始与数组中的下一个值相加。

第二个参数是一个 func,它是一个需要 2 个 int 的 func。

1.total:计算后 func 返回的总和值(聚合值)与之前相同。

2.nextValue: :它是数组序列中的下一个值。然后将该值添加到聚合值(即总计)中。

此外,调试此代码将使您更好地了解聚合的工作原理。

Aggregate is basically used to Group or Sum up data.

According to MSDN
"Aggregate Function Applies an accumulator function over a sequence."

Example 1: Add all the numbers in a array.

int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate((total, nextValue) => total + nextValue);

*important: The initial aggregate value by default is the 1 element in the sequence of collection.
i.e: the total variable initial value will be 1 by default.

variable explanation

total: it will hold the sum up value(aggregated value) returned by the func.

nextValue: it is the next value in the array sequence. This value is than added to the aggregated value i.e total.

Example 2: Add all items in an array. Also set the initial accumulator value to start adding with from 10.

int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);

arguments explanation:

the first argument is the initial(starting value i.e seed value) which will be used to start addition with the next value in the array.

the second argument is a func which is a func that takes 2 int.

1.total: this will hold same as before the sum up value(aggregated value) returned by the func after the calculation.

2.nextValue: : it is the next value in the array sequence. This value is than added to the aggregated value i.e total.

Also debugging this code will give you a better understanding of how aggregate work.

掐死时间 2024-12-07 08:16:15

除了这里已经提供的所有精彩答案之外,我还使用它来引导项目完成一系列转换步骤。

如果转换作为 Func 实现,您可以将多个转换添加到 List> 并使用 聚合以遍历T的实例完成每个步骤。

更具体的示例

您想要获取一个string 值,并让它完成一系列可以通过编程方式构建的文本转换。

var transformationPipeLine = new List<Func<string, string>>();
transformationPipeLine.Add((input) => input.Trim());
transformationPipeLine.Add((input) => input.Substring(1));
transformationPipeLine.Add((input) => input.Substring(0, input.Length - 1));
transformationPipeLine.Add((input) => input.ToUpper());

var text = "    cat   ";
var output = transformationPipeLine.Aggregate(text, (input, transform)=> transform(input));
Console.WriteLine(output);

这将创建一系列转换:删除前导和尾随空格 ->删除第一个字符 ->删除最后一个字符 ->转换为大写。可以根据需要添加、删除或重新排序此链中的步骤,以创建所需的任何类型的转换管道。

此特定管道的最终结果是 " cat " 变为 "A"


一旦您意识到 T 可以是任何东西,这就会变得非常强大。这可以用于图像转换,例如滤镜,以 BitMap 为例;

In addition to all the great answers here already, I've also used it to walk an item through a series of transformation steps.

If a transformation is implemented as a Func<T,T>, you can add several transformations to a List<Func<T,T>> and use Aggregate to walk an instance of T through each step.

A more concrete example

You want to take a string value, and walk it through a series of text transformations that could be built programatically.

var transformationPipeLine = new List<Func<string, string>>();
transformationPipeLine.Add((input) => input.Trim());
transformationPipeLine.Add((input) => input.Substring(1));
transformationPipeLine.Add((input) => input.Substring(0, input.Length - 1));
transformationPipeLine.Add((input) => input.ToUpper());

var text = "    cat   ";
var output = transformationPipeLine.Aggregate(text, (input, transform)=> transform(input));
Console.WriteLine(output);

This will create a chain of transformations: Remove leading and trailing spaces -> remove first character -> remove last character -> convert to upper-case. Steps in this chain can be added, removed, or reordered as needed, to create whatever kind of transformation pipeline is required.

The end result of this specific pipeline, is that " cat " becomes "A".


This can become very powerful once you realize that T can be anything. This could be used for image transformations, like filters, using BitMap as an example;

水波映月 2024-12-07 08:16:15

Jamiec的回答中学到了很多东西。

如果唯一需要生成 CSV 字符串,你可以尝试这个。

var csv3 = string.Join(",",chars);

这是一个使用 100 万个字符串的测试

0.28 seconds = Aggregate w/ String Builder 
0.30 seconds = String.Join 

源代码位于 此处

Learned a lot from Jamiec's answer.

If the only need is to generate CSV string, you may try this.

var csv3 = string.Join(",",chars);

Here is a test with 1 million strings

0.28 seconds = Aggregate w/ String Builder 
0.30 seconds = String.Join 

Source code is here

十级心震 2024-12-07 08:16:15

定义

聚合方法是泛型集合的扩展方法。聚合方法将函数应用于集合的每个项目。不仅应用一个函数,还将其结果作为下一次迭代的初始值。因此,我们将从集合中获得计算值(最小值、最大值、平均值或其他统计值)。

因此,聚合方法是递归函数的一种安全实现形式。

安全,因为递归会迭代集合中的每一项,而我们无法得到任何无穷大由于错误的退出条件而导致循环暂停。 递归,因为当前函数的结果被用作下一个函数调用的参数。

语法:

collection.Aggregate(seed, func, resultSelector);
  • seed - 默认初始值;
  • func - 我们的递归函数。它可以是 lambda 表达式、Func 委托或函数类型 TF(T result, T nextValue);
  • resultSelector - 它可以是一个类似 func 的函数,也可以是一个用于计算、转换、更改、转换最终结果的表达式。

工作原理:

var nums = new[]{1, 2};
var result = nums.Aggregate(1, (result, n) => result + n); //result = (1 + 1) + 2 = 4
var result2 = nums.Aggregate(0, (result, n) => result + n, response => (decimal)response/2.0); //result2 = ((0 + 1) + 2)*1.0/2.0 = 3*1.0/2.0 = 3.0/2.0 = 1.5

实际使用:

  1. 从数字 n 中查找阶乘:

int n = 7;
var numbers = Enumerable.Range(1, n);
var factorial = numbers.Aggregate((result, x) => result * x);

它与此函数执行相同的操作:

public static int Factorial(int n)
{
   if (n < 1) return 1;

   return n * Factorial(n - 1);
}
  1. Aggregate() 是最强大的 LINQ 扩展方法之一,如 Select() 和Where()。我们可以用它来代替Sum()、Min()。 Max()、Avg() 功能,或通过实现附加上下文来更改它:
    var numbers = new[]{3, 2, 6, 4, 9, 5, 7};
    var avg = numbers.Aggregate(0.0, (result, x) => result + x, response => (double)response/(double)numbers.Count());
    var min = numbers.Aggregate((result, x) => (result < x)? result: x);
  1. 扩展方法的更复杂用法:
    var path = @“c:\path-to-folder”;

    string[] txtFiles = Directory.GetFiles(path).Where(f => f.EndsWith(“.txt”)).ToArray<string>();
    var output = txtFiles.Select(f => File.ReadAllText(f, Encoding.Default)).Aggregate<string>((result, content) => result + content);

    File.WriteAllText(path + “summary.txt”, output, Encoding.Default);

    Console.WriteLine(“Text files merged into: {0}”, output); //or other log info

Definition

Aggregate method is an extension method for generic collections. Aggregate method applies a function to each item of a collection. Not just only applies a function, but takes its result as initial value for the next iteration. So, as a result, we will get a computed value (min, max, avg, or other statistical value) from a collection.

Therefore, Aggregate method is a form of safe implementation of a recursive function.

Safe, because the recursion will iterate over each item of a collection and we can’t get any infinite loop suspension by wrong exit condition. Recursive, because the current function’s result is used as a parameter for the next function call.

Syntax:

collection.Aggregate(seed, func, resultSelector);
  • seed - initial value by default;
  • func - our recursive function. It can be a lambda-expression, a Func delegate or a function type T F(T result, T nextValue);
  • resultSelector - it can be a function like func or an expression to compute, transform, change, convert the final result.

How it works:

var nums = new[]{1, 2};
var result = nums.Aggregate(1, (result, n) => result + n); //result = (1 + 1) + 2 = 4
var result2 = nums.Aggregate(0, (result, n) => result + n, response => (decimal)response/2.0); //result2 = ((0 + 1) + 2)*1.0/2.0 = 3*1.0/2.0 = 3.0/2.0 = 1.5

Practical usage:

  1. Find Factorial from a number n:

int n = 7;
var numbers = Enumerable.Range(1, n);
var factorial = numbers.Aggregate((result, x) => result * x);

which is doing the same thing as this function:

public static int Factorial(int n)
{
   if (n < 1) return 1;

   return n * Factorial(n - 1);
}
  1. Aggregate() is one of the most powerful LINQ extension method, like Select() and Where(). We can use it to replace the Sum(), Min(). Max(), Avg() functionality, or to change it by implementing addition context:
    var numbers = new[]{3, 2, 6, 4, 9, 5, 7};
    var avg = numbers.Aggregate(0.0, (result, x) => result + x, response => (double)response/(double)numbers.Count());
    var min = numbers.Aggregate((result, x) => (result < x)? result: x);
  1. More complex usage of extension methods:
    var path = @“c:\path-to-folder”;

    string[] txtFiles = Directory.GetFiles(path).Where(f => f.EndsWith(“.txt”)).ToArray<string>();
    var output = txtFiles.Select(f => File.ReadAllText(f, Encoding.Default)).Aggregate<string>((result, content) => result + content);

    File.WriteAllText(path + “summary.txt”, output, Encoding.Default);

    Console.WriteLine(“Text files merged into: {0}”, output); //or other log info
浅蓝的眸勾画不出的柔情 2024-12-07 08:16:15

这是关于在 Fluent API(例如 Linq 排序)上使用Aggregate 的说明。

var list = new List<Student>();
var sorted = list
    .OrderBy(s => s.LastName)
    .ThenBy(s => s.FirstName)
    .ThenBy(s => s.Age)
    .ThenBy(s => s.Grading)
    .ThenBy(s => s.TotalCourses);

让我们看看我们想要实现一个接受一组字段的排序函数,使用Aggregate而不是for循环非常容易,如下所示:

public static IOrderedEnumerable<Student> MySort(
    this List<Student> list,
    params Func<Student, object>[] fields)
{
    var firstField = fields.First();
    var otherFields = fields.Skip(1);

    var init = list.OrderBy(firstField);
    return otherFields.Skip(1).Aggregate(init, (resultList, current) => resultList.ThenBy(current));
}

我们可以这样使用它:

var sorted = list.MySort(
    s => s.LastName,
    s => s.FirstName,
    s => s.Age,
    s => s.Grading,
    s => s.TotalCourses);

This is an explanation about using Aggregate on a Fluent API such as Linq Sorting.

var list = new List<Student>();
var sorted = list
    .OrderBy(s => s.LastName)
    .ThenBy(s => s.FirstName)
    .ThenBy(s => s.Age)
    .ThenBy(s => s.Grading)
    .ThenBy(s => s.TotalCourses);

and lets see we want to implement a sort function that take a set of fields, this is very easy using Aggregate instead of a for-loop, like this:

public static IOrderedEnumerable<Student> MySort(
    this List<Student> list,
    params Func<Student, object>[] fields)
{
    var firstField = fields.First();
    var otherFields = fields.Skip(1);

    var init = list.OrderBy(firstField);
    return otherFields.Skip(1).Aggregate(init, (resultList, current) => resultList.ThenBy(current));
}

And we can use it like this:

var sorted = list.MySort(
    s => s.LastName,
    s => s.FirstName,
    s => s.Age,
    s => s.Grading,
    s => s.TotalCourses);
所有深爱都是秘密 2024-12-07 08:16:15

用于对多维整数数组中的列求和的聚合

        int[][] nonMagicSquare =
        {
            new int[] {  3,  1,  7,  8 },
            new int[] {  2,  4, 16,  5 },
            new int[] { 11,  6, 12, 15 },
            new int[] {  9, 13, 10, 14 }
        };

        IEnumerable<int> rowSums = nonMagicSquare
            .Select(row => row.Sum());
        IEnumerable<int> colSums = nonMagicSquare
            .Aggregate(
                (priorSums, currentRow) =>
                    priorSums.Select((priorSum, index) => priorSum + currentRow[index]).ToArray()
                );

在聚合函数中使用带索引的 Select 对匹配的列求和并返回一个新数组; { 3 + 2 = 5, 1 + 4 = 5, 7 + 16 = 23, 8 + 5 = 13 }。

        Console.WriteLine("rowSums: " + string.Join(", ", rowSums)); // rowSums: 19, 27, 44, 46
        Console.WriteLine("colSums: " + string.Join(", ", colSums)); // colSums: 25, 24, 45, 42

但是计算布尔数组中 true 的数量更加困难,因为累积类型 (int) 与源类型 (bool) 不同;这里需要一个种子才能使用第二个重载。

        bool[][] booleanTable =
        {
            new bool[] { true, true, true, false },
            new bool[] { false, false, false, true },
            new bool[] { true, false, false, true },
            new bool[] { true, true, false, false }
        };

        IEnumerable<int> rowCounts = booleanTable
            .Select(row => row.Select(value => value ? 1 : 0).Sum());
        IEnumerable<int> seed = new int[booleanTable.First().Length];
        IEnumerable<int> colCounts = booleanTable
            .Aggregate(seed,
                (priorSums, currentRow) =>
                    priorSums.Select((priorSum, index) => priorSum + (currentRow[index] ? 1 : 0)).ToArray()
                );

        Console.WriteLine("rowCounts: " + string.Join(", ", rowCounts)); // rowCounts: 3, 1, 2, 2
        Console.WriteLine("colCounts: " + string.Join(", ", colCounts)); // colCounts: 3, 2, 1, 2

Aggregate used to sum columns in a multi dimensional integer array

        int[][] nonMagicSquare =
        {
            new int[] {  3,  1,  7,  8 },
            new int[] {  2,  4, 16,  5 },
            new int[] { 11,  6, 12, 15 },
            new int[] {  9, 13, 10, 14 }
        };

        IEnumerable<int> rowSums = nonMagicSquare
            .Select(row => row.Sum());
        IEnumerable<int> colSums = nonMagicSquare
            .Aggregate(
                (priorSums, currentRow) =>
                    priorSums.Select((priorSum, index) => priorSum + currentRow[index]).ToArray()
                );

Select with index is used within the Aggregate func to sum the matching columns and return a new Array; { 3 + 2 = 5, 1 + 4 = 5, 7 + 16 = 23, 8 + 5 = 13 }.

        Console.WriteLine("rowSums: " + string.Join(", ", rowSums)); // rowSums: 19, 27, 44, 46
        Console.WriteLine("colSums: " + string.Join(", ", colSums)); // colSums: 25, 24, 45, 42

But counting the number of trues in a Boolean array is more difficult since the accumulated type (int) differs from the source type (bool); here a seed is necessary in order to use the second overload.

        bool[][] booleanTable =
        {
            new bool[] { true, true, true, false },
            new bool[] { false, false, false, true },
            new bool[] { true, false, false, true },
            new bool[] { true, true, false, false }
        };

        IEnumerable<int> rowCounts = booleanTable
            .Select(row => row.Select(value => value ? 1 : 0).Sum());
        IEnumerable<int> seed = new int[booleanTable.First().Length];
        IEnumerable<int> colCounts = booleanTable
            .Aggregate(seed,
                (priorSums, currentRow) =>
                    priorSums.Select((priorSum, index) => priorSum + (currentRow[index] ? 1 : 0)).ToArray()
                );

        Console.WriteLine("rowCounts: " + string.Join(", ", rowCounts)); // rowCounts: 3, 1, 2, 2
        Console.WriteLine("colCounts: " + string.Join(", ", colCounts)); // colCounts: 3, 2, 1, 2
如此安好 2024-12-07 08:16:15

大家都给出了自己的解释。我的解释是这样的。

聚合方法将函数应用于集合的每个项目。例如,让我们有集合 { 6, 2, 8, 3 } 和函数 Add (operator +) 它执行 (((6+2)+8)+3) 并返回 19

var numbers = new List<int> { 6, 2, 8, 3 };
int sum = numbers.Aggregate(func: (result, item) => result + item);
// sum: (((6+2)+8)+3) = 19

在这个例子中,传递了命名方法 Add而不是 lambda 表达式。

var numbers = new List<int> { 6, 2, 8, 3 };
int sum = numbers.Aggregate(func: Add);
// sum: (((6+2)+8)+3) = 19

private static int Add(int x, int y) { return x + y; }

Everyone has given his explanation. My explanation is like that.

Aggregate method applies a function to each item of a collection. For example, let's have collection { 6, 2, 8, 3 } and the function Add (operator +) it does (((6+2)+8)+3) and returns 19

var numbers = new List<int> { 6, 2, 8, 3 };
int sum = numbers.Aggregate(func: (result, item) => result + item);
// sum: (((6+2)+8)+3) = 19

In this example there is passed named method Add instead of lambda expression.

var numbers = new List<int> { 6, 2, 8, 3 };
int sum = numbers.Aggregate(func: Add);
// sum: (((6+2)+8)+3) = 19

private static int Add(int x, int y) { return x + y; }
被翻牌 2024-12-07 08:16:15

一个简短而重要的定义可能是这样的:Linq Aggregate 扩展方法允许声明一种应用于列表元素的递归函数,其操作数有两个:元素按照它们出现在列表中的顺序,一次一个元素,以及前一个递归迭代的结果,或者如果尚未递归则什么也没有。

通过这种方式,您可以计算数字的阶乘,或连接字符串。

A short and essential definition might be this: Linq Aggregate extension method allows to declare a sort of recursive function applied on the elements of a list, the operands of whom are two: the elements in the order in which they are present into the list, one element at a time, and the result of the previous recursive iteration or nothing if not yet recursion.

In this way you can compute the factorial of numbers, or concatenate strings.

我很坚强 2024-12-07 08:16:14

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。然后添加 64 得到 10

示例 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. 使用种子进行数字相乘

为了完整起见,有一个

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 使用字符串连接来创建以逗号分隔的值列表。这是解释聚合的使用的一种简单方法,这也是这个答案的目的。但是,如果使用此技术实际创建大量逗号分隔的数据,那么使用 StringBuilder 会更合适,并且这与使用 Aggregate 完全兼容用于启动 StringBuilder 的种子重载。

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

The easiest-to-understand definition of Aggregate is that it performs an operation on each element of the list taking into account the operations that have gone before. That is to say it performs the action on the first and second element and carries the result forward. Then it operates on the previous result and the third element and carries forward. etc.

Example 1. Summing numbers

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

This adds 1 and 2 to make 3. Then adds 3 (result of previous) and 3 (next element in sequence) to make 6. Then adds 6 and 4 to make 10.

Example 2. create a csv from an array of strings

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

This works in much the same way. Concatenate a a comma and b to make a,b. Then concatenates a,b with a comma and c to make a,b,c. and so on.

Example 3. Multiplying numbers using a seed

For completeness, there is an overload of Aggregate which takes a seed value.

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)

Much like the above examples, this starts with a value of 5 and multiplies it by the first element of the sequence 10 giving a result of 50. This result is carried forward and multiplied by the next number in the sequence 20 to give a result of 1000. This continues through the remaining 2 element of the sequence.

Live examples: http://rextester.com/ZXZ64749
Docs: http://msdn.microsoft.com/en-us/library/bb548651.aspx


Addendum

Example 2, above, uses string concatenation to create a list of values separated by a comma. This is a simplistic way to explain the use of Aggregate which was the intention of this answer. However, if using this technique to actually create a large amount of comma separated data, it would be more appropriate to use a StringBuilder, and this is entirely compatible with Aggregate using the seeded overload to initiate the StringBuilder.

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

Updated example: http://rextester.com/YZCVXV6464

伴我心暖 2024-12-07 08:16:14

它部分取决于您所讨论的重载,但基本思想是:

  • 以种子作为“当前值”开始
  • 迭代序列。对于序列中的每个值:
    • 应用用户指定的函数将 (currentValue,equenceValue) 转换为 (nextValue)
    • 设置currentValue = nextValue
  • 返回最终的 < code>currentValue

您可能会找到 我的 Edulinq 系列中的聚合帖子很有用 - 它包含更详细的描述(包括各种重载)和实现。

一个简单的示例是使用 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有用 - “定制”聚合方法通常对我来说足够好了。

It partly depends on which overload you're talking about, but the basic idea is:

  • Start with a seed as the "current value"
  • Iterate over the sequence. For each value in the sequence:
    • Apply a user-specified function to transform (currentValue, sequenceValue) into (nextValue)
    • Set currentValue = nextValue
  • Return the final currentValue

You may find the Aggregate post in my Edulinq series useful - it includes a more detailed description (including the various overloads) and implementations.

One simple example is using Aggregate as an alternative to 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);

Or perhaps summing all the lengths of strings in a sequence of strings:

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

Personally I rarely find Aggregate useful - the "tailored" aggregation methods are usually good enough for me.

微凉 2024-12-07 08:16:14

超短
聚合的工作方式类似于 Haskell/ML/F# 中的折叠。

稍长
.Max()、.Min()、.Sum()、.Average() 都会迭代序列中的元素,并使用各自的聚合函数聚合它们。 .Aggregate() 是通用聚合器,它允许开发人员指定开始状态(也称为种子)和聚合函数。

我知道您要求简短的解释,但我认为其他人给出了一些简短的答案,我认为您可能会对稍长的答案感兴趣

带有代码的长版本
说明它的作用的一种方法是展示如何在使用 foreach 和使用 .Aggregate 时实现示例标准差注意:我在这里没有优先考虑性能,因此我不必要地对集合进行多次迭代

首先使用一个辅助函数来创建二次距离之和:

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

然后使用 .Aggregate 一次:

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

恕我直言

那么 .Aggregate 有助于可读性吗?总的来说,我喜欢 LINQ,因为我认为 .Where、.Select、.OrderBy 等对可读性有很大帮助(如果您避免内联分层 .Selects)。出于完整性原因,Aggregate 必须位于 Linq 中,但我个人不太相信 .Aggregate 与编写良好的 foreach 相比增加了可读性。

Super short
Aggregate works like fold in Haskell/ML/F#.

Slightly longer
.Max(), .Min(), .Sum(), .Average() all iterates over the elements in a sequence and aggregates them using the respective aggregate function. .Aggregate () is generalized aggregator in that it allows the developer to specify the start state (aka seed) and the aggregate function.

I know you asked for a short explaination but I figured as others gave a couple of short answers I figured you would perhaps be interested in a slightly longer one

Long version with code
One way to illustrate what does it could be show how you implement Sample Standard Deviation once using foreach and once using .Aggregate. Note: I haven't prioritized performance here so I iterate several times over the colleciton unnecessarily

First a helper function used to create a sum of quadratic distances:

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

Then Sample Standard Deviation using 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));
}

Then once using .Aggregate:

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

Note that these functions are identical except for how sumOfQuadraticDistance is calculated:

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

Versus:

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

So what .Aggregate does is that it encapsulates this aggregator pattern and I expect that the implementation of .Aggregate would look something like this:

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

Using the Standard deviation functions would look something like this:

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

IMHO

So does .Aggregate help readability? In general I love LINQ because I think .Where, .Select, .OrderBy and so on greatly helps readability (if you avoid inlined hierarhical .Selects). Aggregate has to be in Linq for completeness reasons but personally I am not so convinced that .Aggregate adds readability compared to a well written foreach.

拥有 2024-12-07 08:16:14

一张图胜过一千个字

提醒:
Func 是一个具有两个 XY 类型输入的函数,返回 类型的结果R.

Enumerable.Aggregate 具有三个重载:

重载 1:

A Aggregate<A>(this IEnumerable<A> a, Func<A, A, A> f)

Aggregate1

示例:

new[]{1,2,3,4}.Aggregate((x, y) => x + y);  // 10

这种重载很简单,但有以下限制:

  • 序列必须至少包含一个元素,
    否则该函数将抛出InvalidOperationException
  • 元素和结果必须属于同一类型。


重载 2:

B Aggregate<A, B>(this IEnumerable<A> a, B bIn, Func<B, A, B> f)

Aggregate2

示例:

var hayStack = new[] {"straw", "needle", "straw", "straw", "needle"};
var nNeedles = hayStack.Aggregate(0, (n, e) => e == "needle" ? n+1 : n);  // 2

这种重载更为通用:

  • 必须提供种子值 (bIn)。
  • 集合可以为空,
    在这种情况下,该函数将产生种子值作为结果。
  • 元素和结果可以有不同的类型。


重载 3:

C Aggregate<A,B,C>(this IEnumerable<A> a, B bIn, Func<B,A,B> f, Func<B,C> f2)

在我看来,第三个重载并不是很有用。
通过使用重载 2,后跟一个转换其结果的函数,可以更简洁地编写相同的内容。

插图改编自这篇优秀的博文

A picture is worth a thousand words

Reminder:
Func<X, Y, R> is a function with two inputs of type X and Y, that returns a result of type R.

Enumerable.Aggregate has three overloads:

Overload 1:

A Aggregate<A>(this IEnumerable<A> a, Func<A, A, A> f)

Aggregate1

Example:

new[]{1,2,3,4}.Aggregate((x, y) => x + y);  // 10

This overload is simple, but it has the following limitations:

  • the sequence must contain at least one element,
    otherwise the function will throw an InvalidOperationException.
  • elements and result must be of the same type.


Overload 2:

B Aggregate<A, B>(this IEnumerable<A> a, B bIn, Func<B, A, B> f)

Aggregate2

Example:

var hayStack = new[] {"straw", "needle", "straw", "straw", "needle"};
var nNeedles = hayStack.Aggregate(0, (n, e) => e == "needle" ? n+1 : n);  // 2

This overload is more general:

  • a seed value must be provided (bIn).
  • the collection can be empty,
    in this case, the function will yield the seed value as result.
  • elements and result can have different types.


Overload 3:

C Aggregate<A,B,C>(this IEnumerable<A> a, B bIn, Func<B,A,B> f, Func<B,C> f2)

The third overload is not very useful IMO.
The same can be written more succinctly by using overload 2 followed by a function that transforms its result.

The illustrations are adapted from this excellent blogpost.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文