2009-07-26 13 views
5

Próbuję wymyślić zapytania LINQ do konwertowania IEnumerable<int> do innego IEnumerable<int>, gdzie każdy int w wyniku jest sumą wszystkich wskazówki do tej pozycji od wstępnej listy:Jak obliczyć sumę roboczą serii int w zapytaniu Linq?

Given int[] a
muszę int[] b
Gdzie b[0] = a[0], b[1] = a[0] + a[1], b[2] = a[0] + a[1] + a[2] i tak dalej

Alternatywnie, kwoty powyżej można zapisać jako b[1] = b[0] + a[1], b[2] = b[1] + a[2] i tak dalej, ale nie widzę w jaki sposób pomoże.

mogę, oczywiście, to zrobić z for pętli, ale uzyskanie w [] sekwencji z zapytania i myślałem, że wyglądają ładniej, jeśli nadal tego zapytania zamiast nagle dodając tam :)

się for

Odpowiedz

13

Cóż, można to zrobić z efektami ubocznymi dość łatwo, choć jest to dość icky ...

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

byłoby miło, gdyby ramy przewidziane w rodzaju „uruchomiony agregat” do hermetyzacji to, ale to nie jest tak daleko, jak wiem.

+0

Jesteś absolutnie fantastyczne :) Tak, to jest nieco icky, ale jest wystarczająco dobry, a ja jestem na dążeniu do usunięcia do sprawozdania ostatnio. –

8

Napisałem funkcję, aby zrobić to jakiś czas temu. Jest podobny do funkcji Haskella o nazwie 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

Masz problem z 'zwrotem zwrotu po raz pierwszy', * pierwszym * jeśli typu T, nie TResult –

5

Alternatywą do rozwiązania pana Skeet: jeśli mamy spadek wymogu kwerendy LINQ i bardziej dosłownie zająć „przekształcić IEnumerable<int> do innego IEnumerable<int>” możemy użyć to:

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

które można zastosować do nieskończonej serii:

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

Jest to również przydatne, jeśli nie chcesz utworzyć całej tablicy naraz.

+0

Tak, użyłem składni tablic, ponieważ łatwiej było wyjaśnić wymagania, ale argument jest w rzeczywistości IEnumerable . Jednak Pan Skeet nadal działa w tym przypadku, po prostu bez wywołania .ToArray() i jest bardziej kompaktowy, więc nadal głosuję na to :) –

+0

Miałem na myśli "rozwiązanie pana Skeeta", oczywiście: P –

0

Powyższa odpowiedź nie działa poprawnie ... i nie ma tej samej sygnatury, co skanowanie Haskella .... zasadniczo jest rozszerzeniem idei "agregatu" linq ... Myślę, że to pasuje do realizacja Haskell lepiej

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

wykorzystanie

[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); 
    } 
Powiązane problemy