我想计算我的功能,其中一些功能最多使用三个参数。现在我使用相同的代码,并为这三个代码添加一些变体。
let GetTime f (args : string) =
let sw = Stopwatch.StartNew()
f (args)
printfn "%s : %A" sw.Elapsed
我想用这个替换这三个函数。
let GetTime f ( args : 'T[]) =
let sW = Stopwatch.StartNew()
match args.Length with
| 1 -> f args.[0]
| 2 -> f (args.[0] args.[1])
printfn "%A" sW.Elapsed
()
但是如果我使用它运行的三个函数,我会收到类型不匹配的错误。是否可以将函数作为参数发送并像这样使用它?
答案 0 :(得分:5)
为什么不做这样的事情?
let getTime f =
let sw = Stopwatch.StartNew()
let result = f ()
printfn "%A" sw.Elapsed
result
假设f1
,f2
和f3
是分别采用1,2和3个参数的三个函数,您可以使用getTime
函数,如下所示:
getTime (fun () -> f1 "foo")
getTime (fun () -> f2 "foo" "bar")
getTime (fun () -> f3 "foo" "bar" "baz")
但是,如果您只需要在FSI中计算某些功能,则此功能已内置:只需键入
> #time;;
并且时间将打开。
答案 1 :(得分:4)
编译器无法知道在运行时将传递多少个参数,因此函数f
必须同时满足'T -> unit
和'T -> 'T -> unit
。此表单还要求所有参数都属于同一类型。
以下方法会延迟功能执行,可能适合您的需要。
let printTime f =
let sw = Stopwatch.StartNew()
f() |> ignore
printfn "%A" sw.Elapsed
let f1 s = String.length s
let f2 s c = String.concat c s
printTime (fun () -> f1 "Test")
printTime (fun () -> f2 [| "Test1"; "Test2" |] ",")
答案 2 :(得分:2)
您可能正在考虑将方法组作为参数传递给GetTime
,然后让编译器决定要调用的方法组的重载。任何.NET编译器都无法做到这一点。方法组用于编译器和工具(如ReSharper)的代码分析,但它们不是在运行时实际存在的东西。
答案 3 :(得分:2)
如果您的函数以 tupled 形式使用其参数,请执行以下操作:
let f1 (s: string, b: bool) =
System.Threading.Thread.Sleep 1000
s
let f2 (n: int, s:string, dt: System.DateTime) =
System.Threading.Thread.Sleep 1000
n+1
然后实施变得微不足道:
let Timed f args =
let sw = System.Diagnostics.Stopwatch.StartNew()
let ret = f args
printfn "Called with arguments %A, elapsed %A" args sw.Elapsed
ret
用法:
f1
|> Timed // note, at this time we haven't yet applied any arguments
<| ("foo", true)
|> printfn "f1 done, returned %A"
f2
|> Timed
<| (42, "bar", DateTime.Now)
|> printfn "f2 done, returned %A"
但是,如果函数以 curried 形式使用它们的参数,就像这样:
let f1Curried (s: string) (b: bool) =
System.Threading.Thread.Sleep 1000
s
let f2Curried (n: int) (s:string) (dt: System.DateTime) =
System.Threading.Thread.Sleep 1000
n+1
它变得有点棘手。我们的想法是使用旨在取消参数的标准运算符(<|)
,(<||)
和(<|||)
。
let Timed2 op f args =
let sw = System.Diagnostics.Stopwatch.StartNew()
let ret = op f args
printfn "Called with arguments %A, elapsed %A" args sw.Elapsed
ret
f1Curried
|> Timed2 (<||) // again, no arguments are passed yet
<| ("foo", true)
|> printfn "f1Curried done, returned %A"
f2Curried
|> Timed2 (<|||)
<| (42, "bar", DateTime.Now)
|> printfn "f2Curried done, returned %A"