代码之家  ›  专栏  ›  技术社区  ›  Pierre-olivier Gendraud

用LINQ有效地验证累积和验证属性

  •  0
  • Pierre-olivier Gendraud  · 技术社区  · 6 年前

    我想验证一下我的列表中元素(非负)的和是否与某些值有关。我不想计算整个和,这是不必要的。(如果我们证明第一个元素的和不尊重这个性质,我们就停止计算)

    所以我想要一个LINQ命令来验证累加和中的每个元素都低于某个值,只要它看到不等式成立。

    var b = a.Aggregate(new List<int> { 0 }, (ls, x) => { ls.Add(x + ls.Last()); return ls; }).All(x => x < 4);
    

    这种方法不行。当看到累加和的第i个元素不安全,但整个累加和是计算出来的时,所有的操作都停止。

    你有更好的办法吗?(我知道我们可以用loop有效地做到这一点,但我想用LINQ)

    如果我使用循环:

    var s = 0;

    var b = true;

    foreach(var x in list) { s=s+x; if(s>4){ b= false; break;} }

    谢谢你

    2 回复  |  直到 6 年前
        1
  •  3
  •   Panagiotis Kanavos    6 年前

    你不需要使用LINQ方法来做你想做的事情。您可以使用枚举器和循环编写自己的代码。毕竟,LINQ to对象操作本身是使用循环实现的。例如 TakeWhile 作为循环源并生成匹配元素的迭代器实现:

        static IEnumerable<TSource> TakeWhileIterator<TSource>(IEnumerable<TSource> source, Func<TSource, int, bool> predicate) {
            int index = -1;
            foreach (TSource element in source) {
                checked { index++; }
                if (!predicate(element, index)) break;
                yield return element;
            }
        }
    

    缺点是,这会为迭代器生成一个状态机,并返回所有匹配的元素,无论是否使用它们。

    您可以编写自己的扩展方法来计算循环中的和并返回 true 如果循环在未达到限制的情况下完成:

    public static bool SumBelow(this IEnumerable<int> source, int limit) 
    {
        int sum=0;
        foreach (var element in source) 
        {
            sum+=element;
            if (sum>limit)
            {
                return false;
            }
        }
        return true;
    }
    

    并将其用作扩展方法:

    var isSumBelow = someEnumerable.SumBelow(5);
    

    为什么不使用通用方法?

    无法指定运算符约束或可寻址接口,这就是为什么 Sum() 每种类型都单独实现自身,例如:

    public static int Sum(this IEnumerable<int> source) {
        if (source == null) throw Error.ArgumentNull("source");
        int sum = 0;
        checked {
            foreach (int v in source) sum += v;
        }
        return sum;
    }
    

    功能方式

    将累加器和条件检查器作为函数传递可以用于创建一个通用的、可重用的方法,该方法可以处理任何转换和条件:

    public static bool AccWithinLimit<T>(
                            this IEnumerable<T> source, 
                            Func<T,T,T> accumulator,
                            Func<T,bool> terminator, 
                            T seed=default) 
    {
        T total=seed;
        foreach (var element in source) 
        {
            total = accumulator(element,total);            
            if (terminator(total))
            {
                return false;
            }
        }
        return true;
    }
    

    这可用于检查整数数组的部分和:

    var myArray=new []{1,2,3};
    var limit = 5;
    var totalBelowLimit = myArray.AccWithinLimit(myArray,
                                      (sum,elm)=>sum+elm,
                                      sum=>sum>limit);
    

    或部分产品的双倍列表:

    var myList = new List<double>{1.0, 2.0, 3.0};
    var limit = 10;
    var totalBelowLimit = myList.AccWithinLimit(myArray,
                                      (sum,elm)=>sum*elm,
                                      sum=>sum>limit,
                                      1);
    
        2
  •  2
  •   Mel Gerats    6 年前

    您可以使用TakeWhile从列表中获取项目,直到总和超出某个值

    public void TestTakeWhileCumulativeSum()
    {
        int[] numbers = new[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
        int maxCumulativeSum = 5;
        int previous = 0;
        var result = numbers.TakeWhile(n => (previous = n + previous) <= maxCumulativeSum);
        Assert.AreEqual(result.Count(), 5);
    }