【问题标题】:Unoptimized IEnumerable<>.Last with predicate未优化的 IEnumerable<>.Last 与谓词
【发布时间】:2013-12-05 16:32:10
【问题描述】:

IEnumerable&lt;TSource&gt;public static TSource Last&lt;TSource&gt;(this IEnumerable&lt;TSource&gt; source) 扩展方法对 IList&lt;TSource&gt; 类型的源进行了优化,因此当它可以使用索引进入末尾时,它不会遍历整个序列。

IList<TSource> tSources = source as IList<TSource>;
if (tSources != null)
{
    int count = tSources.Count;
    if (count > 0)
    {
        return tSources[count - 1];
    }
}

我稍微修改了代码,使其更易于阅读,但功能保持不变。

public static TSource Last&lt;TSource&gt;(this IEnumerable&lt;TSource&gt; source, Func&lt;TSource, bool&gt; predicate) 可以清楚地从序列末尾开始迭代时,为什么它不进行优化?

如果应该有与谓词匹配的东西并且它靠近序列的开头,那么我仍然需要迭代到最后。如果最后有匹配的东西,我不必重复更多,因为我是从最后开始的。

我希望这样的东西成为该方法的一部分。

IList<TSource> tSources = source as IList<TSource>;
if (tSources != null)
{
    int count = tSources.Count;
    if (count > 0)
    {
        for (int i = count - 1; i >= 0; i--)
        {
            if (predicate(tSources[i]))
            {
                return tSources[i];
            }
        }
    }
}

【问题讨论】:

  • 有许多优化是可以执行的。例如,Reverse 可以针对列表进行优化,但不是。与以往的任何其他功能一样,答案是要么他们没有想到该功能,要么他们认为不值得花时间实施。
  • 难以置信。我使用 .NET 已经有几年了,我特别喜欢它,因为它们不会发布半生不熟的功能。我什至听过 C# 设计团队的某个人的采访,他甚至说了这句话。此外,当他们优化更多方法时,他们不能简单地忽略这一点。
  • 所以你认为这个方法是半生不熟的,只是因为它没有这个功能?意识到像 C# 这样的语言有数以万计的提议特性需要被拒绝。只有很小一部分被考虑的功能可以实现。有 很多 被拒绝的功能实际上可能相当有用。团队根本没有无限的资源。
  • 还要注意,这不仅会改变性能,还会改变方法的功能。如果谓词有副作用,您将更改哪些项目调用了谓词,以及调用的顺序。这可能是个问题(尽管希望人们不会依赖它)。
  • 据我所知,我对此有什么误解?在我看来,您不知道满足条件的最后一项是在开头还是结尾,要找出哪个,无论如何您都必须遍历所有元素。我错过了什么?

标签: c# .net linq extension-methods ienumerable


【解决方案1】:

我的猜测是,你可能会得到任何答案:优化谓词情况会导致更多副作用。优化通常会导致副作用,因为未调用 IEnumarble 的 GetEnumerator()。但是在谓词的情况下,您可以从外部观察到它不是按顺序调用的。有人可能会编写一个谓词,期望迭代按顺序发生,而倒退会破坏它。

【讨论】:

    【解决方案2】:

    除非IList&lt;T&gt; 的实现包含非常少的项目,优化非谓词Last 以返回theList[theList.Count()-1] 不太可能比从头开始简单枚举慢;对于任何合理的IList&lt;T&gt; 实现,它不太可能会慢得多。尽管 IList&lt;T&gt; 的某些实现按索引查找项目所需的时间可能明显长于按顺序获取数据所需的每个项目的平均时间(可能是一个数量级,但可能不是两个),阅读 一个项按索引通常比顺序读取每个项花费的时间要少得多(随机访问通常不会比顺序访问慢很多,除非集合很大;如果在 1,000,000 个项目的集合中,按索引读取项目的成本是顺序访问所需的每个项目时间的 1,000 倍 [一个非常大的惩罚],那仍然是读取每个项目所需时间的 1,000 倍项目)。

    Last 与谓词一起使用时,无法保证需要检查多少项。如果随机访问比顺序访问花费更长的时间(常见),并且最后一个匹配恰好发生在列表的早期(非常合理),那么从头处理列表将比从尾处理更快。鉴于 IList&lt;T&gt; 实现执行随机访问的时间是顺序获取的十倍并不是不合理的,从后面读取的“优化”最终可能会使事情比“未优化的”顺序读取的代码。

    如果IList&lt;T&gt; 提供了更多读取数据的选项,和/或包含粗略描述不同操作的相对成本的属性,则可以优化Last 方法以检查其他顺序的列表项,但不存在对于此类功能,该方法最好使用一种直接的方法,如果没有启发性,其行为是可预测的,而不是一种试图聪明但有时可能表现得更差的方法。

    【讨论】:

      猜你喜欢
      • 2022-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-13
      • 2018-08-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多