我们什么时候应该使用FSharpFunc.Adapt?

Joe*_*ler 17 f#

看一下FSharp.Core和PowerPack中的源代码,我看到很多接受带有两个或更多参数的函数的高阶函数都使用FSharpFunc.Adapt.例如:

let mapi f (arr: ResizeArray<_>) =
   let f = FSharpFunc<_,_,_>.Adapt(f)
   let len = length arr
   let res = new ResizeArray<_>(len)
   for i = 0 to len - 1 do
       res.Add(f.Invoke(i, arr.[i]))
   res
Run Code Online (Sandbox Code Playgroud)

文档FSharpFunc.Adapt相当薄.这是我们应该在任何时候使用具有类似签名的高阶函数时使用的一般最佳实践吗?只有多次调用传入函数?它的优化程度是多少?我们应该Adapt在任何地方使用,或者只是很少使用?

谢谢你的时间.

Tom*_*cek 14

那很有意思!我没有任何官方信息(我在任何地方都没有看到这些信息),但是这里有一些关于Adapt函数如何工作的想法.

函数类似于函数的mapicurried形式,这意味着参数的类型被编译为类似的东西FSharpFunc<int, FSharpFunc<T, R>>.但是,许多函数实际上直接编译为两个参数的函数,因此实际值通常是FSharpFunc<int, T, R>继承的函数FSharpFunc<int, FSharpFunc<T, R>>.

如果你调用这个函数(例如f 1 "a"),F#编译器生成这样的东西:

FSharpFunc<int, string>.InvokeFast<a>(f, 1, "a");
Run Code Online (Sandbox Code Playgroud)

如果你InvokeFast使用Reflector 查看函数,你会看到它测试函数是否被编译为优化版本(f :? FSharpFunc<int, T, R>).如果是,则直接调用Invoke(1, "a"),如果不是,则需要进行两次调用Invoke(1).Invoke("a").

每次调用作为参数传递的函数时都会执行此检查(执行检查然后使用优化调用可能会更快,因为这更常见).

Adapt函数的作用是将任何函数转换为FSharpFunc<T1, T2, R>(如果函数未被优化,它会为它创建一个包装器,但大多数时候情况并非如此).对自适应函数的调用将更快,因为它们不需要每次都进行动态检查(检查仅在内部进行一次Adapt).

因此,Adapt如果您正在调用作为参数传递的函数,那么总结就是可以提高性能,该函数多次使用多于1个参数.与任何优化一样,我不会盲目地使用它,但在调整性能时要注意这一点是一件有趣的事情!

(顺便说一下:感谢一个非常有趣的问题,我不知道编译器会这样做:-))