Generics iter比本机映射慢:有通用解决方案吗?

Generics iter比本机映射慢:有通用解决方案吗?,generics,compiler-construction,f#,inline,functor,Generics,Compiler Construction,F#,Inline,Functor,这个问题以前已经出现过,其原因由John Palmer回答: 让ar=Array.zeroCreate(64*1024*1024) #时间 //Real:00:00.171,CPU:00:00.171,GC gen0:0,gen1:0,gen2:0 因为我在0。。ar.长度-1 do ar[i]Array.iteri(fun i->ar[i]Seq.iteri(fun i->ar[i]对于iter,您可以检查收集的类型并将其发送到最佳实现。遇到问题的是map,它需要返回T。这需要更高类型的类型

这个问题以前已经出现过,其原因由John Palmer回答:

让ar=Array.zeroCreate(64*1024*1024)
#时间
//Real:00:00.171,CPU:00:00.171,GC gen0:0,gen1:0,gen2:0
因为我在0。。ar.长度-1 do

ar[i]Array.iteri(fun i->ar[i]Seq.iteri(fun i->ar[i]对于
iter
,您可以检查收集的类型并将其发送到最佳实现。遇到问题的是
map
,它需要返回
T
。这需要更高类型的类型,而F#不直接支持。下面是
map
的一个尝试,说明了问题:

module AnySeq =
  let map<'a, 'b, 'c, 'd when 'a :> seq<'b> and 'd :> seq<'c>> (f: 'b -> 'c) (s: 'a) : 'd =
    let (!) x = unbox (box x)
    let tyDef = s.GetType().GetGenericTypeDefinition()
    if tyDef = typedefof<list<_>> then !(List.map f !s)
    elif tyDef = typedefof<array<_>> then !(Array.map f !s)
    else !(Seq.map f !s)
您得到一个值限制错误:

已推断值“floats”具有泛型类型val floats:“\u a when”\u a:>seq


如果您将类型注释添加到
floats
,那么它是有效的,但是您还没有完成任何事情。

确实,您在F#中没有更高级的类型,但是您仍然可以使用内联函数并模拟某些类型类行为。 通过定义函子,您可以获得本机映射的通用解决方案:

type Fmap = Fmap with
    static member ($) (_Functor:Fmap, x:List<_> ) = fun f -> List.map    f x  
    static member ($) (_Functor:Fmap, x:array<_>) = fun f -> Array.map   f x
    static member ($) (_Functor:Fmap, x:_ [,]   ) = fun f -> Array2D.map f x
    static member ($) (_Functor:Fmap, x:_ [,,]  ) = fun f -> Array3D.map f x
    static member ($) (_Functor:Fmap, x:_ [,,,] ) = fun f ->
        Array4D.init (x.GetLength 0) (x.GetLength 1) (x.GetLength 2) (x.GetLength 3) (fun a b c d -> f x.[a,b,c,d])

let inline fmap f x = (Fmap $ x) f
类型Fmap=带有
静态成员($)(_函子:Fmap,x:List)=fun f->List.map f x
静态成员($)(_函子:Fmap,x:array)=fun f->array.map f x
静态成员($)(_函子:Fmap,x:[,])=fun f->Array2D.map f x
静态成员($)(_函子:Fmap,x:[,])=fun f->Array3D.map f x
静态成员($)(_函子:Fmap,x:[,,])=fun f->
Array4D.init(x.GetLength 0)(x.GetLength 1)(x.GetLength 2)(x.GetLength 3)(fun a b c d->fx[a,b,c,d])
让内联fmap f x=(fmap$x)f
以fmap为例,这个函数是内联的,它将接受重载中定义的任何类型,并且它将以直接调用函数的速度执行(这实际上是在幕后发生的)。 为了实现iter,您可以使用fmap并放弃结果,在您的情况下,您可能需要定义类似fmapi的内容以提供索引

请记住,若您传递一个Seq,那个么应该始终使用具体类型调用这些函数 你知道理论上存在的令人满意的解决方案吗?(会有什么花哨的名字?)

你不能既有蛋糕又吃

是否有一些语言能够很好地承认并解决这个问题


F#承认并很好地解决了这个问题。你可以像F#一样有一个清晰的性能配置文件和快速编译,也可以像F#一样有一个不可预测的性能配置文件和缓慢的编译,这是因为一个非常复杂的编译器装载了针对这种特殊情况的优化过程。F#选择了前者(实用)解决方案。

您可以使用更高级的泛型来实现这一点,而F#不支持。Haskell和Scala都支持,类型类似于
Iterable c=>CA->(a->())->()
然后,您可以为
数组
Seq
和任何其他集合类型提供实现。您可能会感兴趣。您想做什么,使通用
映射
iter
函数对所有集合都是最佳的?我的猜测是,如果可能的话,F#不会有单独的模块,例如h如
数组
列表
Seq
。编译器确实进行了一些优化-检查第1027行
Seq.Length
的实现。尽管它不需要为
映射/iter
类型函数费心“您可以使用更高级的泛型来实现这一点,而F#不支持这一点。Haskell和Scala都支持"。这个评论毫无意义。这个问题问的是在F#中完成的某些操作的性能。因此,显然,在F#中,您可以在没有更高级泛型的情况下执行此操作。此外,Haskell在这种情况下的速度是出了名的不可预测的慢,正是因为这种语言功能leadi,它将快速算术运算转变为缓慢的虚拟调度ng对类似这样的错误的处理:JonHarrop,Lee,谢谢你的评论。我认为它们都有意义,观点不同。@JohnPalmer,谢谢你提供的信息链接,我发现自己对源代码看得不够多,不像ruby,在ruby中,从eval环境访问源代码要容易得多……所以这是答案,理论上都是如此实际上,更高种类的类型,加上f#代码。然而,我想Jon也有一个更高关注点。关于一个更一般的问题,你有一个非常好的观点。如果我能标记两个答案,我也会标记这个。
let ints = List.init 10000000 id
let floats = ints |> AnySeq.map float
type Fmap = Fmap with
    static member ($) (_Functor:Fmap, x:List<_> ) = fun f -> List.map    f x  
    static member ($) (_Functor:Fmap, x:array<_>) = fun f -> Array.map   f x
    static member ($) (_Functor:Fmap, x:_ [,]   ) = fun f -> Array2D.map f x
    static member ($) (_Functor:Fmap, x:_ [,,]  ) = fun f -> Array3D.map f x
    static member ($) (_Functor:Fmap, x:_ [,,,] ) = fun f ->
        Array4D.init (x.GetLength 0) (x.GetLength 1) (x.GetLength 2) (x.GetLength 3) (fun a b c d -> f x.[a,b,c,d])

let inline fmap f x = (Fmap $ x) f