将以前的值与IEnumerable相加

我有一系列数字:

var seq = new List { 1, 3, 12, 19, 33 }; 

我想将其转换为一个新的序列,其中数字被添加到前面的数字以创建一个新的序列:

 { 1, 3, 12, 19, 33 } --> {1, 4, 16, 35, 68 } 

我想出了以下内容,但我不喜欢状态变量’count’。 我也不喜欢这样一个事实,即我使用值Enumerable而不采取行动。

 int count = 1; var summed = values.Select(_ => values.Take(count++).Sum()); 

怎么可能呢?

这是函数式编程中的常见模式,在F#中称为扫描 。 它就像C#的Enumerable.Aggregate和F#的折叠,除了它产生累加器的中间结果以及最终结果。 我们可以使用扩展方法很好地在C#中实现扫描:

 public static IEnumerable Scan(this IEnumerable input, Func next, U state) { yield return state; foreach(var item in input) { state = next(state, item); yield return state; } } 

然后使用如下:

 var seq = new List { 1, 3, 12, 19, 33 }; var transformed = seq.Scan(((state, item) => state + item), 0).Skip(1); 

“Pure”LINQ:

var result = seq.Select((a, i) => seq.Take(i + 1).Sum());

一个“纯”LINQ O(n):

 var res = Enumerable.Range(0, seq.Count) .Select(a => a == 0 ? seq[a] : seq[a] += seq[a - 1]); 

还有一个LINQ,具有状态维护:

 var tmp = 0; var result = les.Select(a => { tmp += a; return tmp; }); 
 var seq = new List { 1, 3, 12, 19, 33 }; var summed = new List(); seq.ForEach(i => summed.Add(i + summed.LastOrDefault())); 

要使用Linq,只能在使用自定义聚合器后迭代列表:

 class Aggregator { public List List { get; set; } public int Sum { get; set; } } 

..

 var seq = new List { 1, 3, 12, 19, 33 }; var aggregator = new Aggregator{ List = new List(), Sum = 0 }; var aggregatorResult = seq.Aggregate(aggregator, (a, number) => { a.Sum += number; a.List.Add(a.Sum); return a; }); var result = aggregatorResult.List; 
 var seq = new List { 1, 3, 12, 19, 33 }; for (int i = 1; i < seq.Count; i++) { seq[i] += seq[i-1]; } 

只是为了提供另一种选择,尽管不是LINQ,你可以编写一个基于yield的函数来进行聚合:

 public static IEnumerable SumSoFar(this IEnumerable values) { int sumSoFar = 0; foreach (int value in values) { sumSoFar += value; yield return sumSoFar; } } 

像BrokenGlass一样,这只会对数据进行一次传递,尽管与他的返回不同,迭代器不是列表。

(令人讨厌的是, 您无法轻松地在列表中的数字类型上进行此通用 。)

Stephen Swensen的答案很棒,扫描正是您所需要的。 虽然不需要种子,但还有另一种扫描版本,这对于您的确切问题更为合适。

此版本要求您的输出元素类型与输入元素类型相同(在您的情况下),并且优点是不需要传入0然后跳过第一个(0)结果。

您可以在C#中实现此版本的扫描,如下所示:

 public static IEnumerable Scan(this IEnumerable Input, Func Accumulator) { using (IEnumerator enumerator = Input.GetEnumerator()) { if (!enumerator.MoveNext()) yield break; T state = enumerator.Current; yield return state; while (enumerator.MoveNext()) { state = Accumulator(state, enumerator.Current); yield return state; } } } 

然后使用如下:

 IEnumerable seq = new List { 1, 3, 12, 19, 33 }; IEnumerable transformed = seq.Scan((state, item) => state + item);