我有一个以下代码,它将数组的每个元素转换为之前所有元素的总和。程序实施如下:
float[] items = {1, 5, 10, 100}; //for example
float[] sums = new float[items.Length];
float total = 0;
for(int i = 0; i < items.Length; i++){
total+=items[i];
sums[i] = total;
}
我如何将其作为LINQ单线程实现? 我知道它可以做到例如
items.Select((x, i) => items.Take(i + 1).Sum())
但我认为当数组大小增加时效率不高,因为它必须对每个元素执行Sum()。
答案 0 :(得分:5)
LINQ并不是非常干净地支持这种情况,说实话 - 你想要混合聚合和投影。你可以做副作用,这很可怕:
// Don't use this!
float sum = 0f;
var sums = items.Select(x => sum +=x).ToArray();
LINQ中的副作用令人讨厌。同样,可以使用Take
/ Sum
来执行此操作,如RePierre和LB所示 - 但这需要自然 O(N)的操作并将其转换为O(N ^ 2)的操作。
我刚开始的MoreLINQ project确实在其Scan
和PreScan
成员中对此有所支持。在这种情况下,您需要Scan
,我相信:
var sums = items.Scan((x, y) => x + y);
如果您不想使用第三方库,不希望使用副作用,不希望Take
解决方案效率低下,仅 >需要添加,只需要一个类型(例如你的float
)你可以轻松地介绍自己的方法:
public static IEnumerable<float> RunningSum(this IEnumerable<float> source)
{
if (source == null)
{
throw new ArgumentNullException(source);
}
float sum = 0f;
foreach (var item in source)
{
sum += item;
yield return sum;
}
}
正如您已经注意到的,这基本上与您的原始代码相同 - 但是被懒惰地评估并适用于任何浮动序列。
答案 1 :(得分:1)
var result = items.Select((item, index) => items.Take(index).Sum() + item);
修改强>
您可以使用Aggregate
方法创建总和:
var result = items.Aggregate(new List<float>(), (seed, item) =>
{
seed.Add(seed.LastOrDefault() + item);
return seed;
});
答案 2 :(得分:1)
Microsoft的Reactive Extensions团队发布了一个“Interactive Extensions”库,为IEnumerable<T>
添加了许多有用的扩展。其中一个是Scan
,它完全符合您的要求。
以下是执行总计的IX方式:
IEnumerable<float> results = items.Scan(0.0f, (x1, x2) => x1 + x2);