Получить сумму коллекции IEnumerable только в одном выражении LINQ - PullRequest
2 голосов
/ 18 мая 2011

Предположим, у меня есть генератор inifite A().Я хочу получить сумму всех чисел, возвращаемых A, чтобы сумма не превышала значение N только в одном выражении LINQ .

I'mинтересно, есть ли метод расширения, который поможет мне с этим?

Классический способ будет:

int sum = 0;
foreach (int x in A()) {
    sum += x;
    if (sum > N) {
        break;
    }
}

return sum;

, но я думал о том, как сделать это только в одном выражениибез успеха ...

Ответы [ 7 ]

3 голосов
/ 18 мая 2011

Конечно, есть способ сделать это с помощью одного выражения LINQ. Самое простое, что я мог бы придумать и все же иметь некоторую универсальность и элегантность:

public static int SumWhile(this IEnumerable<int> collection, Func<int, bool> condition)
{
    int sum = 0;
    foreach (int i in collection)
    {
        sum += i;
        if (!condition(sum))
            break;
    }
    return sum;
}

который можно назвать как:

int sum = A().SumWhile(i => i <= N);

Да, только одно выражение LINQ! Веселитесь вместе с ним

3 голосов
/ 18 мая 2011

Используя стандартный идиоматический LINQ, это было бы невозможно.Семантика, в которой вы нуждаетесь, представляет собой комбинацию Aggregate() и TakeWhile().В противном случае вам понадобятся побочные эффекты, которые в LINQ запрещены.

Вот пример одного из способов сделать это с побочными эффектами:

var temp = 0;
var sum = A().TakeWhile(i =>
{
    var res = !(temp > N);
    temp += i;
    return res;
}).Sum();
3 голосов
/ 18 мая 2011

Если A является бесконечным генератором, то это невозможно сделать в одном выражении, используя только встроенные методы LINQ.

Чтобы сделать это чисто, в одном выражении, без побочных эффектов, вам, вероятно, понадобится какой-то метод Scan для вычисления суммы префикса входной последовательности. Тогда вам просто нужен первый элемент больше, чем N. Легко!

int sum = A().Scan((s, x) => s + x).First(s => s > N);

// ...

public static class EnumerableExtensions
{
    public static IEnumerable<T> Scan<T>(
        this IEnumerable<T> source, Func<T, T, T> func)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (func == null) throw new ArgumentNullException("func");

        using (var e = source.GetEnumerator())
        {
            if (e.MoveNext())
            {
                T accumulator = e.Current;
                yield return accumulator;

                while (e.MoveNext())
                {
                    accumulator = func(accumulator, e.Current);
                    yield return accumulator;
                }
            }
        }
    }
}
1 голос
/ 22 мая 2011

Вероятно, наиболее близко к вашей первоначальной идее:

int sum = 0;
int limit = 500;
A().TakeWhile(i => (sum += i) < limit).Count();
//Now the variable named sum contains the smaller sum of elements being >= limit

Count () используется не для возвращаемого значения, а для принудительного фактического перечисления.

0 голосов
/ 18 мая 2011

Я считаю, что приведенный ниже ужасающий код удовлетворяет вашим требованиям. : -)

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace ConsoleApplication12 {
  public class Program {
    public static void Main(string[] args) {
      const int N=100;

      int sum;
      try {
        sum=A().Aggregate((self, next) => {
          if(self+next<=N)
            return self+next;
          else
            throw new ResultException(self);
        });
      } catch(ResultException re) {
        sum=re.Value;
      }
      Debug.Print("Sum="+sum);
    }

    private class ResultException : Exception {
      public readonly int Value;

      public ResultException(int value) {
        Value=value;
      }
    }

    private static IEnumerable<int> A() {
      var i=0;
      while(true) {
        yield return i++;
      }
    }
  }
}
0 голосов
/ 18 мая 2011

int sum = A (). Где (x => x

0 голосов
/ 18 мая 2011

Посмотрим, правильно ли я выполняю требования.

A () - бесконечный генератор.По определению, он генерирует значения (в данном случае целые числа) навсегда.

Вы хотите найти все значений, которые меньше N, и сложить их вместе.

Линк не проблема.Вы не закончите добавление, пока A () не завершит генерацию ... и это никогда не произойдет.

Кстати, код, который вы разместили, не все значения меньше N ... этоскладывает все значения до тех пор, пока не найдет одно меньше N, а затем перестанет искать.Это то, что вы имели в виду?

...