【问题标题】:IEnumerable.Count() O(n)IEnumerable.Count() O(n)
【发布时间】:2011-12-05 17:52:31
【问题描述】:

我只是偶然发现了这段代码,我想知道为什么 Count 在循环期间完成。

  /// <summary>
  /// find the first index in a sequence to satisfy a condition
  /// </summary>
  /// <typeparam name="T">type of elements in source</typeparam>
  /// <param name="source">sequence of items</param>
  /// <param name="predicate">condition of item to find</param>
  /// <returns>the first index found, or -1 if not found</returns>
  public static int FindIndex<T>(this IEnumerable<T> source, Predicate<T> predicate)
  {
         for (int i = 0; i < source.Count(); i++)
         {
               if (predicate(source.ElementAt(i))) return i;
         }
         return -1; // Not found
  }

如果计数可以改变,我们不应该这样做吗:for (int i = source.Count() - 1; i >= 0; i--)

否则,我认为我们应该在循环开始之前计算计数,而不是每次。

这样做的正确方法是什么?

【问题讨论】:

    标签: c# linq ienumerable


    【解决方案1】:

    为此编写手动代码的正确方法是丢掉所有垃圾并简单地使用foreach 进行迭代:

    public static int FindIndex<T>(this IEnumerable<T> source, Predicate<T> predicate) 
    {
        var index = 0;
        foreach(var item in source) {
            if(predicate(item)) {
                return index;
            }
            ++index;
        }
    
        return -1;
    } 
    

    【讨论】:

      【解决方案2】:

      这是真的糟糕的代码,可能 - 它可能迭代所有 O (n^2),因为 Count() 可能 需要迭代(如果它没有t 实现 ICollection-of-T/IList-of-T 以提供 .Count,这是经过测试的),并且 ElementAt(x) 可能需要迭代 x 个项目,因为它可能没有用于索引器的 IList-of-T。它可能适用于列表/数组,但在一般情况下它提供了糟糕的性能。最坏的情况是它迭代所有 n 项 n 次(对于 Count()s),加上伸缩/三角形系列,再给出 n(n+1)/2 个增量(对于 ElementAt()s)。

      只需使用 foreach。或者更好的是,寻找现有的 IndexOf 方法。

      【讨论】:

        【解决方案3】:

        如果您打算使用 LINQ,那么为什么不全部使用 LINQ? LINQ 的设计目的是在这种情况下特别容易使用。

        【讨论】:

        • 他如何使用 LINQ 比foreach 更干净地完成此任务? IEnumerable 不一定有顺序,因此 LINQ 没有针对 IEnumerable 的任何内置的面向索引的功能。
        • 我打算提供一个 LINQ 替代方案,但在这种情况下它变得笨拙:您需要使用 Select 投影到“项目 + 索引”匿名类型,然后按谓词过滤,然后返回第一个匹配或 -1 如果没有。直 foreach 更干净简洁。
        • @Brian:是的,确实如此;大多数投影和选择函数都提供提供“索引”的重载(就像 Jon 的示例中的计数器)。这并不是说这是 正确的 方法,而是说 LINQ 不知道索引是不正确的。
        • LINQ 的想法突然出现在我的脑海中,因为我最近一直在处理一堆 FP。我承认foreach 更直接,而且 LINQ 有点笨拙,但还不错:return source.Select((x, i) =&gt; new Tuple&lt;T, int&gt;(x, i)).Aggregate(-1, (acc, x) =&gt; acc == -1 &amp;&amp; predicate(x.Item1) ? x.Item2 : acc);(或任何其他做同样事情的方法)
        • @KenWayneVanderLinde:关于它并没有那么糟糕......恕我直言,如果你将两个 sn-ps 并排放置,代码将不言自明。
        【解决方案4】:

        这是使用 Linq 的 O(n) 方式:

        public static int FindIndex<T>(this IEnumerable<T> source, Predicate<T> pred)
        {
            var foundItem = 
                items
                    .Select( (i,index) => new {i,index} )
                    .FirstOrDefault( x => pred(x.i));
            return foundItem == null ? -1 : foundItem.index;
        }
        

        【讨论】:

          猜你喜欢
          • 2021-04-24
          • 2011-12-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多