3

我想为我的函数计时,其中一些函数最多使用三个参数。现在我在下面使用相同的代码,但对这三个代码有一些变化。

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
    ()

但是如果我使用它工作的三个函数,我会收到类型不匹配的错误。是否可以将函数作为参数发送并像这样使用它?

4

4 回答 4

5

为什么不做这样的事情呢?

let getTime f =
    let sw = Stopwatch.StartNew()
    let result = f ()
    printfn "%A" sw.Elapsed
    result

假设f1f2f3是三个分别接受 1、2 和 3 个参数的函数,您可以getTime像这样使用该函数:

getTime (fun () -> f1 "foo")
getTime (fun () -> f2 "foo" "bar")
getTime (fun () -> f3 "foo" "bar" "baz")

但是,如果您只需要对 FSI 中的某些功能进行计时,则此功能已经内置:只需键入

> #time;;

并且计时将被打开。

于 2014-11-21T07:23:00.663 回答
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" |] ",")
于 2014-11-21T04:14:58.910 回答
2

您可能正在考虑将方法组作为参数传递给GetTime,然后让编译器决定要调用的方法组的哪个重载。任何 .NET 编译器都无法做到这一点。方法组被编译器和 ReSharper 等工具用于代码分析,但它们并不是运行时实际存在的东西。

于 2014-11-21T03:56:31.367 回答
2

如果您的函数采用元组形式的参数,如下所示:

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"

但是,如果函数采用柯里化形式的参数,如下所示:

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"
于 2014-11-23T02:50:46.210 回答