2009-07-26 66 views
5

我想拿出一个LINQ查询到IEnumerable<int>转换为另一种IEnumerable<int>,在结果中的每个int是所有整数的总和,将其从初始列表中的位置:如何计算Linq查询中一系列整数的运行总和?

鉴于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循环,但我从查询获得一个[]序列,我认为它看起来更好,如果我继续这样查询,而不是突然加入for那里:)

回答

13

好了,你可以用副作用做到这一点很轻松了,虽然这是很恶心......

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

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

+0

你是绝对精彩:)是的,它是有点恶心,但它的不够好,我在寻求最近删除的语句。 – 2009-07-26 15:22:53

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(); 
+1

你在`yield return first`有问题,* first * if type T,not TResult – 2016-06-21 18:13:43

5

到飞碟双向先生的解决方案的另一种:如果我们下降的要求对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); 

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

+0

是的,我使用了数组语法,因为它更容易解释需求,但参数实际上是一个IEnumerable 。然而,Skeet先生仍然在这种情况下工作,没有.ToArray()调用,它更紧凑,所以我仍然投票支持那个:) – 2009-07-26 19:58:34

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); 
    }