2009-07-26 44 views
5

Tôi cố gắng để đưa ra một truy vấn LINQ để chuyển đổi một IEnumerable<int>-IEnumerable<int> khác, trong đó mỗi int trong kết quả là tổng của tất cả các ints lên đến vị trí đó từ danh sách ban đầu:Làm cách nào để tính tổng chạy của chuỗi int trong truy vấn LINQ?

Với int[] a
tôi cần int[] b
đâu b[0] = a[0], b[1] = a[0] + a[1], b[2] = a[0] + a[1] + a[2] và vân vân

Ngoài ra, số tiền trên có thể được viết như b[1] = b[0] + a[1], b[2] = b[1] + a[2] và như vậy, nhưng tôi không thấy như thế nào mà có thể giúp. Tôi có thể, tất nhiên, làm điều này với một vòng lặp for, nhưng tôi nhận được một chuỗi [] từ một truy vấn và tôi nghĩ nó sẽ đẹp hơn nếu tôi tiếp tục truy vấn đó thay vì đột ngột thêm for ở đó :)

Trả lời

13

Vâng, bạn có thể làm điều đó với tác dụng phụ dễ dàng đủ, mặc dù nó khá icky ...

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

nó sẽ được tốt đẹp nếu khuôn khổ cung cấp một loại "chạy tổng hợp" để đóng gói này, nhưng nó không xa như tôi biết.

+0

Bạn đang hoàn toàn tuyệt vời :) Vâng, nó là hơi icky, nhưng nó đủ tốt, và tôi đang trên một nhiệm vụ để loại bỏ các báo cáo cho thời gian gần đây. –

8

Tôi đã viết một hàm để thực hiện việc này một thời gian trước đây. Nó tương tự như chức năng scanl của Haskell.

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

Bạn có vấn đề tại «lợi nhuận đầu tiên', * đầu tiên * nếu kiểu T, không phải TResult –

5

Một thay thế cho giải pháp của ông Skeet: Nếu chúng ta thả các yêu cầu cho một truy vấn LINQ và nghĩa hơn giải quyết "chuyển đổi một IEnumerable<int>-IEnumerable<int> khác" chúng ta có thể sử dụng này:

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

mà chúng tôi có thể áp dụng cho chuỗi vô hạn:

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

Điều này cũng hữu ích nếu bạn không muốn tạo toàn bộ mảng cùng một lúc.

+0

Có, tôi đã sử dụng cú pháp mảng vì nó dễ dàng hơn để giải thích các yêu cầu, nhưng đối số thực sự là một số điện thoại có thể truy cập là . Tuy nhiên, ông Skeet vẫn làm việc trong trường hợp đó, chỉ cần không có cuộc gọi .ToArray(), và nó nhỏ gọn hơn, vì vậy tôi vẫn bỏ phiếu cho điều đó :) –

+0

Tôi có nghĩa là "giải pháp của ông Skeet", tất nhiên: P –

0

Câu trả lời ở trên không hoàn toàn hoạt động .... và không chia sẻ cùng một chữ ký như quét của Haskell .... về cơ bản là phần mở rộng cho ý tưởng về "tổng hợp" của LINQ ... Tôi nghĩ điều này phù hợp với Haskell thực hiện tốt hơn

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

sử dụng

[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); 
    } 
Các vấn đề liên quan