此问题已经出现过,其理由由John Palmer解答: why is Seq.iter and Seq.map so much slower?
let ar = Array.zeroCreate<int> (64*1024*1024)
#time
//Real: 00:00:00.171, CPU: 00:00:00.171, GC gen0: 0, gen1: 0, gen2: 0
for i in 0 .. ar.Length - 1 do
ar.[i] <- ar.[i]*3
//Real: 00:00:00.327, CPU: 00:00:00.328, GC gen0: 0, gen1: 0, gen2: 0
ar |> Array.iteri(fun i _ -> ar.[i] <- ar.[i]*3)
//Real: 00:00:02.249, CPU: 00:00:02.250, GC gen0: 0, gen1: 0, gen2: 0
ar |> Seq.iteri(fun i _ -> ar.[i] <- ar.[i]*3)
我想知道是否存在某种“内联”或其他可以映射的通用机制,比如序列(最后已知的?)具体类型以加速这些行为。 例如,我有静态保证,我将迭代一个数组。
您是否知道理论上存在令人满意的解决方案? (那里有什么花哨的名字?)
是否有一些能够很好地承认和解决这个问题的语言?
答案 0 :(得分:4)
你确实在F#中没有高级绑定类型,但你仍然可以使用内联函数并模拟一些类型类行为。 通过定义Functor,您可以获得原生映射的通用解决方案:
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并丢弃结果,在您的情况下,您可能需要定义类似fmapi的内容以使索引可用。
请记住,如果您传递Seq&lt;'a&gt;,则应始终使用具体类型调用这些函数。它会失败,你不应该混合使用两种方法:subtype和ad-hoc多态。
答案 1 :(得分:2)
您是否知道理论上存在令人满意的解决方案? (那里有什么花哨的名字?)
你不能吃蛋糕并且吃它。
是否有一些能够很好地承认和解决这个问题的语言?
F#承认了这个问题并很好地解决了这个问题。您可以拥有清晰的性能配置文件和快速编译,如F#,或者您可能会有一个不可预测的性能配置文件和缓慢的编译,因为大量复杂的编译器负载优化传递,如此特殊情况。 F#选择了前(实用)解决方案。
答案 2 :(得分:0)
对于iter
,您可以检查集合的类型并分派到最佳实现。遇到问题的地方是map
,有些T<'b>
需要返回T<'a>
。这需要更高的kinded类型,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)
如果您尝试使用它将int list
转换为float list
:
let ints = List.init 10000000 id
let floats = ints |> AnySeq.map float
您收到了值限制错误:
值'float'被推断为具有泛型类型的val浮点数:'_ a when'_a:&gt; SEQ
如果您向floats
添加类型注释,它会起作用,但之后您还没有完成任何操作。