这个使用 Linq 的 c# 方法有什么问题?

What is wrong with this c# method that uses Linq?

我尝试编写一个方法,给定一个 int Enumerable 和一个 int 变量(我们称它为 n),它生成所有总和小于 n 的子序列。

事情是这样的:

public static IEnumerable<IEnumerable<int>> SubSWithSumSmallerThanN(this IEnumerable<int> intEnum, int n)
    {
        var numbers = intEnum.ToList();
        return Enumerable
            .Range(1, numbers.Count)
            .SelectMany(length => Enumerable.Range(0, numbers.Count - length + 1)
                                            .Select(x => numbers.TakeWhile(y => numbers.IndexOf(y) > x && numbers.IndexOf(y) < x + length)))
                                            .Where(x => x.Sum() <= n)
            .ToArray();
    }

基本上,这行不通。举个简单的例子,like

{1, 5, 3, 8}, n = 7

而结果应该是

{{1}, {5}, {3}, {1, 5}}

实际结果是这样的:

Expected: List<IEnumerable<Int32>> [[1], [5], [3], [1, 5]]
Actual:   IEnumerable`1[] []

它生成一个几乎为空的 IEnumerable。我该如何解决?

您可以很容易地使用 for 循环编写解决方案:

public static IEnumerable<IEnumerable<int>> SubsequencesLessThen(IReadOnlyList<int> input, int limit)
{
    for (int i = 0; i < input.Count; i++)
    {
        int sum = 0;
        for (int j = i; j < input.Count; j++)
        {
            sum += input[j];
            if (sum >= limit)
                break;

            yield return input.Skip(i).Take(j - i + 1);
        }
    }
}

我可以仅使用 linq 得出的最接近的等价物是:

public static IEnumerable<IEnumerable<int>> SubsequencesLessThen2(IReadOnlyList<int> input, int limit)
{
    return from i in Enumerable.Range(0, input.Count)
        from j in Enumerable.Range(i, input.Count - i)
        let elements = input.Skip(i).Take(j - i + 1)
        let sum = elements.Sum()
        where sum <= limit
        select elements;
}

这是相当昂贵的:我们不会在注意到总和超过限制时立即停止,所以这将计算 1+5 然后 1+5+3 然后 1+5+3+8 然后 5+3,依此类推。我们还每次都重新计算总和,而不是保持总计 运行。