为什么Seq模块中的某些功能已经优化而其他功能不在F#中?

the*_*onk 8 collections performance f#

这是我之前关于模块和函数与模块等效项相比要慢得多的问题的后续内容.SeqitermapArrayList

查看源代码,我可以看到一些函数,例如isEmptylength执行非常简单的类型检查,以便在使用之前优化数组和列表IEnumerator.

[<CompiledName("IsEmpty")>]
let isEmpty (source : seq<'T>)  = 
    checkNonNull "source" source
    match source with 
    | :? ('T[]) as a -> a.Length = 0
    | :? list<'T> as a -> a.IsEmpty
    | :? ICollection<'T> as a -> a.Count = 0
    | _ -> 
        use ie = source.GetEnumerator()
        not (ie.MoveNext())

[<CompiledName("Length")>]
let length (source : seq<'T>)    = 
    checkNonNull "source" source
    match source with 
    | :? ('T[]) as a -> a.Length
    | :? ('T list) as a -> a.Length
    | :? ICollection<'T> as a -> a.Count
    | _ -> 
        use e = source.GetEnumerator() 
        let mutable state = 0 
        while e.MoveNext() do
            state <-  state + 1;
        state
Run Code Online (Sandbox Code Playgroud)

在采用iter相同的方法可以大大提高其性能的情况下,当我对该函数进行阴影处理时,iter它比内置版本显着提升:

[<CompiledName("Iterate")>]
let iter f (source : seq<'T>) = 
    checkNonNull "source" source
    use e = source.GetEnumerator()
    while e.MoveNext() do
        f e.Current;
Run Code Online (Sandbox Code Playgroud)

我的问题是,因为一些在功能Seq模块被用于与特定集合类型使用优化的(阵列,列表<T>等)如何来其他功能,例如iternth以类似的方式未优化?

另外,在map函数的情况下,正如@mausch指出的那样,是否不可能采用类似的方法Enumerable.Select(见下文)并为不同的集合类型构建专门的迭代器?

public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
    {
      if (source == null)
        throw Error.ArgumentNull("source");
      if (selector == null)
        throw Error.ArgumentNull("selector");
      if (source is Enumerable.Iterator<TSource>)
        return ((Enumerable.Iterator<TSource>) source).Select<TResult>(selector);
      if (source is TSource[])
        return (IEnumerable<TResult>) new Enumerable.WhereSelectArrayIterator<TSource, TResult>((TSource[]) source, (Func<TSource, bool>) null, selector);
      if (source is List<TSource>)
        return (IEnumerable<TResult>) new Enumerable.WhereSelectListIterator<TSource, TResult>((List<TSource>) source, (Func<TSource, bool>) null, selector);
      else
        return (IEnumerable<TResult>) new Enumerable.WhereSelectEnumerableIterator<TSource, TResult>(source, (Func<TSource, bool>) null, selector);
    }
Run Code Online (Sandbox Code Playgroud)

提前谢谢了.

Dan*_*iel 1

Seq.length从表面上看, /中的类型检查isEmpty似乎是错误。我假设大多数Seq函数不执行此类正交性检查:List/Array模块中已存在特定于类型的版本。为什么要重复它们?

这些检查在 LINQ 中更有意义,因为它只IEnumerable直接使用。

  • 至少在“Seq.length”的情况下,优化有可能将 O(n) 操作变成 O(1) 操作。我怀疑这是一个错误。 (5认同)
  • 不,我不知道答案,但是对于“Seq.iter”、“Seq.map”等,进行类型测试不会改变算法的渐近复杂度,只会改变常数因子。 (4认同)
  • 这取决于您期望什么样的输入。如果您从外部源(例如库)获取“seq”,那么您会需要这些增强功能,这与 LINQ 的原因相同。 (2认同)