如何在Linq查询中计算一系列整数的运行总和?

时间:2009-07-26 15:14:42

标签: c# linq

我正在尝试使用linq查询将IEnumerable<int>转换为另一个IEnumerable<int>,其中结果中的每个int都是从初始列表到该位置的所有整数的总和:

鉴于int[] a
我需要int[] b
b[0] = a[0], b[1] = a[0] + a[1], b[2] = a[0] + a[1] + a[2]等等

或者,上面的总和可以写成b[1] = b[0] + a[1], b[2] = b[1] + a[2],依此类推,但我不知道这会有什么帮助。

当然,我可以使用for循环执行此操作,但是我从查询中获取a []序列,我认为如果我继续查询而不是突然添加{{{ 1}}那里:))

4 个答案:

答案 0 :(得分:15)

嗯,你可以很容易地做副作用,虽然它很蠢......

int sum = 0;
int[] b = a.Select(x => (sum += x)).ToArray();

如果框架提供了一种“运行聚合”来封装它,那将是很好的,但它并不是我所知道的。

答案 1 :(得分:8)

我刚才写了一个函数来做这件事。它类似于Haskell的scanl函数。

public static IEnumerable<TResult> Scan<T, TResult>(
    this IEnumerable<T> source, 
    Func<T, T, TResult> combine)
{
    using (IEnumerator<T> data = source.GetEnumerator())
        if (data.MoveNext())
        {
            T first = data.Current;

            yield return first;

            while (data.MoveNext())
            {
                first = combine(first, data.Current);
                yield return first;
            }
        }
}

int[] b = a
    .Scan((running, current) => running + current)
    .ToArray();

答案 2 :(得分:5)

Skeet先生解决方案的替代方案:如果我们放弃了对linq查询的要求,并且更确切地说是“将IEnumerable<int>转换为另一个IEnumerable<int>”,我们可以使用:

    static IEnumerable<int> Sum(IEnumerable<int> a)
    {
        int sum = 0;
        foreach (int i in a)
        {
            sum += i;
            yield return sum;
        }
    }

我们可以应用于无限系列:

    foreach (int i in Sum(MyMath.NaturalNumbers))
        Console.WriteLine(i);

如果您不想一次创建整个数组,这也很有用。

答案 3 :(得分:0)

上面的答案并不完全正常....并且与Haskell的scanl没有共享相同的签名.... 基本上它是对linq“聚合”概念的扩展......我认为这更符合Haskell的实现

    public static IEnumerable<TResult> Scanl<T, TResult>(
        this IEnumerable<T> source,
        TResult first,
        Func<TResult, T, TResult> combine)
    {
        using (IEnumerator<T> data = source.GetEnumerator())
        {
            yield return first;

            while (data.MoveNext())
            {
                first = combine(first, data.Current);
                yield return first;
            }
        }
    }

使用

    [TestMethod]
    public void Scanl_Test()
    {
        var xs = new int[] { 1, 2, 3, 4, 5, 6, 7 };

        var lazyYs = xs.Scanl(0, (y, x) => y + x);

        var ys = lazyYs.ToArray();

        Assert.AreEqual(ys[0], 0);
        Assert.AreEqual(ys[1], 1);
        Assert.AreEqual(ys[2], 3);
        Assert.AreEqual(ys[3], 6);
        Assert.AreEqual(ys[4], 10);
        Assert.AreEqual(ys[5], 15);
        Assert.AreEqual(ys[6], 21);
        Assert.AreEqual(ys[7], 28);
    }