将函数作为参数传递并使其超载

时间:2014-11-21 00:42:39

标签: f# parametric-polymorphism

我想计算我的功能,其中一些功能最多使用三个参数。现在我使用相同的代码,并为这三个代码添加一些变体。

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 个答案:

答案 0 :(得分: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;;

并且时间将打开。

答案 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"