2009-07-26 8 views
5

私はlinqクエリで、IEnumerable<int>を別のIEnumerable<int>に変換しようとしています。結果の各intは、最初のリストからその位置までのすべてのintの合計です:どのようにLinqクエリで一連の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 []シーケンスを取得して、突然forをそこに追加するのではなく、クエリを続行すればより美しく見えます:)

答えて

13

それはかなり不快だが、さて、あなたは、簡単に十分な副作用でそれを行うことができます...

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

フレームワークは、これをカプセル化するために、「実行中の集約」のようなものを提供する場合、それはいいだろうが、それ私が知っている限りではありません。

+0

あなたは本当に素晴らしい:)はい、それはやや不快であるが、それは十分に良いことだ、と私は最近のステートメントのために削除するための探求にしています。 –

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 returns first'に問題があります。* TResultではなくT型の場合は* first * –

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()呼び出しがなくてもまだ動作しています。コンパクトなので、私はまだそれに投票しています:) –

+0

私は「Mr. Skeetの解決策」を意味しました。 –

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); 
    } 
関連する問題