博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
LINQ聚合算法解释
阅读量:3578 次
发布时间:2019-05-20

本文共 9874 字,大约阅读时间需要 32 分钟。

这可能听起来很蹩脚,但我还没有找到一个关于Aggregate的非常好的解释。

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


#1楼

Aggregate主要用于分组或汇总数据。

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

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

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

* important:默认情况下,初始聚合值是集合序列中的1个元素。 即:默认情况下,总变量初始值为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的函数。

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

2.nextValue ::它是数组序列中的下一个值。 将该值加到聚合值即总数上。

同时调试此代码将使您更好地了解聚合的工作方式。


#2楼

从回答中学到了很多东西。

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

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

这是一个包含100万个字符串的测试

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

源代码在


#3楼

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

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


#4楼

这是关于在Fluent API上使用Aggregate的解释,例如Linq Sorting。

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

并且让我们看看我们想要实现一个带有一组字段的sort函数,这很容易使用Aggregate而不是for循环,如下所示:

public static IOrderedEnumerable
MySort( this List
list, params Func
[] 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);

#5楼

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

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
rowSums = nonMagicSquare .Select(row => row.Sum()); IEnumerable
colSums = nonMagicSquare .Aggregate( (priorSums, currentRow) => priorSums.Select((priorSum, index) => priorSum + currentRow[index]).ToArray() );

在Aggregate func中使用带索引的select来对匹配的列求和并返回一个新的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

但是计算布尔数组中的trues数量更加困难,因为累积类型(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
rowCounts = booleanTable .Select(row => row.Select(value => value ? 1 : 0).Sum()); IEnumerable
seed = new int[booleanTable.First().Length]; IEnumerable
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

#6楼

一张图片胜过千言万语

提醒:

Func<X, Y, R>是一个具有两个XY类型输入的函数,它返回R类型的结果。

Enumerable.Aggregate有三个重载:

超载1:

A Aggregate(IEnumerable a, Func
f)

Aggregate1

例:

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

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

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


超载2:

B Aggregate
(IEnumerable
a, B bIn, Func
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
(IEnumerable
a, B bIn, Func
f, Func
f2)

第三次超载对IMO来说不是很有用。

通过使用重载2后跟一个转换其结果的函数,可以更简洁地编写相同的内容。

插图改编自 。


#7楼

除了这里已经有的所有优秀答案之外,我还使用它来完成一系列转换步骤。

如果转换是作为Func<T,T> ,则可以向List<Func<T,T>>添加几个转换List<Func<T,T>>并使用Aggregate在每个步骤中遍历T的实例。

一个更具体的例子

您希望获取string值,并通过一系列可以以编程方式构建的文本转换进行操作。

var transformationPipeLine = new List
>();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为例;


#8楼

每个人都给出了他的解释。 我的解释是这样的。

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

var numbers = new List
{ 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
{ 6, 2, 8, 3 };int sum = numbers.Aggregate(func: Add);// sum: (((6+2)+8)+3) = 19private static int Add(int x, int y) { return x + y; }

#9楼

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

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

您可能会发现的很有用 - 它包含更详细的描述(包括各种重载)和实现。

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


#10楼

最容易理解的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,bc来制作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个元素。

实例: :

文档: :


附录

上面的示例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);

更新示例: :


#11楼

超短聚合在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
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
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
( this IEnumerable
values, TAggregate seed, Func
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,因为我认为。在哪里,。选择,.OrderBy等大大提高了可读性(如果你避免内联的hierarhical。选择)。 由于完整性原因,Aggregate必须在Linq中,但我个人并不相信.Aggregate与一个写得很好的foreach相比增加了可读性。

转载地址:http://geogj.baihongyu.com/

你可能感兴趣的文章
MyBatis-Plus代码生成器
查看>>
我的第一个SpringBoot项目(一)
查看>>
回文数
查看>>
伪背包问题!
查看>>
求10000以内n的阶乘!
查看>>
static关键字
查看>>
类的继承
查看>>
final关键字
查看>>
抽象类
查看>>
java的多态现象
查看>>
java中对象的类型转换
查看>>
java基础入门 String
查看>>
Java基础入门 StringBuffer类
查看>>
Java基础入门 currentTimeMillis方法
查看>>
Java基础入门 arraycopy方法
查看>>
Java基础入门 Math类
查看>>
Java基础入门 Random类
查看>>
Java基础入门 Date类
查看>>
Java基础入门 Calendar类
查看>>
Java基础入门 DateFormat类
查看>>