将IEnumerable中的先前值相加

Rit*_*ton 16 .net c# linq timtowtdi

我有一系列数字:

var seq = new List<int> { 1, 3, 12, 19, 33 };
Run Code Online (Sandbox Code Playgroud)

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

{ 1, 3, 12, 19, 33 } --> {1, 4, 16, 35, 68 }
Run Code Online (Sandbox Code Playgroud)

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

int count = 1;
var summed = values.Select(_ => values.Take(count++).Sum());
Run Code Online (Sandbox Code Playgroud)

怎么可能呢?

Ste*_*sen 20

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

public static IEnumerable<U> Scan<T, U>(this IEnumerable<T> input, Func<U, T, U> next, U state) {
    yield return state;
    foreach(var item in input) {
        state = next(state, item);
        yield return state;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后使用如下:

var seq = new List<int> { 1, 3, 12, 19, 33 };
var transformed = seq.Scan(((state, item) => state + item), 0).Skip(1);
Run Code Online (Sandbox Code Playgroud)

  • 注意:可以在Reactive/Interactive Extensions中找到的添加的Enumerable运算符不包含种子值作为Scan序列的第一个值,这与上面的不同. (5认同)

Kir*_*huk 8

"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]);
Run Code Online (Sandbox Code Playgroud)

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

var tmp = 0;
var result = les.Select(a => { tmp += a; return tmp; });
Run Code Online (Sandbox Code Playgroud)

  • 我不喜欢O(n ^ 2).你应该能够线性地做到这一点. (4认同)
  • @Roly,它取决于数组大小.如果数组大小是5个元素,我将使用我的代码:-) (2认同)