将嵌套的for循环转换为LINQ表达式

3
请考虑以下代码:
    private ISet<int> CalcSumsOfTwoNums(IEnumerable<int> nums) {
        ISet<int> iset = new HashSet<int>();
        var asArray = nums.ToArray();

        for (var i = 0; i < asArray.Length - 2; i++) {
            for (var j = i; j < asArray.Length - 1; j++) {
                var sum = asArray[i] + asArray[j];
                if (sum <= MAX) {
                    iset.Add(sum);
                }
            }
        }

        return iset;
    }

将嵌套的 for 循环语法转换为 LINQ 表达式或 LINQ 点记号,这是否有意义?这是其中一种情况,for 循环语法更合适吗?我的倾向是说,在这里使用 for 循环更好,因为我依赖于数组的索引位置,通过它来获取结果集。


1
也许您应该解释一下循环实际上是做什么的,这样会更容易理解吧? - MarcinJuraszek
据我所知,Linq 的性能与常规循环相当。 - WiiMaxx
@MarcinJuraszek - 我认为我试图发现的最重要的事情是,在上面的for循环语法中,保存数组索引的ij变量是计算总和的关键,而我不知道如何将其转换为LINQ语法。 nums中的值可以是任意的,例如1到10。我返回一个ISet而不是一个IEnumerable,以确保返回的值是唯一的。 - λ Jonas Gorauskas
你为什么要首先使用LINQ呢?它确实适用于某些操作,但乍一看,我会说这不是其中之一。也许这是有意的,但在for循环中限制的情况下,数组的最后一个值将不会被使用。 - Dirk
1
@WiiMaxx:LINQ通常由于涉及lambda函数而具有较差的性能。它们看起来很简单,但实际上在实现中相当复杂。对于所有情况都不会有影响。 - Dirk
显示剩余3条评论
2个回答

2
private static ISet<int> CalcSumsOfTwoNums2(IEnumerable<int> nums)
{
    // get List<int> from nums to get info about collection length
    var source = nums.ToList();

    // proper iteration
    var data = source.Take(source.Count - 1)
                     .SelectMany((e, ix) => source.Skip(ix)
                                                  .Take(source.Count - 1 - ix)
                                                  .Select(i => new { e, i }))
                     .Select(x => x.e + x.i)
                     .Where(x => x < MAX);

    // return HashSet instead of IEnumerable<int>
    return new HashSet<int>(data);
}

这个方法返回的结果和你的方法是相同的。然而,我建议你保留你当前的解决方案。它的性能可能比 LINQ 更好。


上面的代码有一个小错误,就是在最后一行的 Where 子句中,应该将 x < MAX 改为 x <= MAX - λ Jonas Gorauskas
我选择这个答案作为被接受的答案,因为MarcinJuraszek的算法避免了计算每个总和两次的额外工作。此外,这个答案运行速度大约快2.5倍,但不比“for”循环语法更快。 - λ Jonas Gorauskas

1
您可以尝试以下 Linq 代码:

var asArray = nums.ToArray();
var result = (from n1 in asArray.Take(asArray.Length - 2)
              from n2 in asArray.Take(asArray.Length - 1)
              where n1 + n2 <= MAX
              select n1 + n2);

iset = new HashSet<int>(result);

它的输出与您的代码片段相同。

内部循环仍然从该集合的开头迭代。问题示例从外部循环的实际位置开始内部循环:var j = i。结果是相同的,但您的解决方案中存在一些不必要的迭代。 - MarcinJuraszek
@MarcinJuraszek - 请再次检查! - Parimal Raj
1
结果还不错,因为 HashSet 将重复的项合并在一起 :) 但是你计算每个总和都计算了两次:作为 (i1,i2) 和 (i2,i1)。 - MarcinJuraszek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接