Передайте функцию в качестве параметра и перегрузите ее

Я хочу определить время моих функций, некоторые из них используют до трех параметров. Прямо сейчас я использую тот же код ниже с некоторыми вариациями для трех.

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 ответа

Решение

Компилятор не может знать, сколько аргументов будет передано во время выполнения, поэтому функция 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" |] ",")

Почему бы просто не сделать что-то подобное?

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;;

и время будет включено.

Вы, вероятно, думаете о передаче группы методов в качестве аргумента GetTimeи затем компилятор решает, какую перегрузку группы методов вызывать. Это невозможно с любым компилятором.NET. Группы методов используются для анализа кода компиляторами и такими инструментами, как ReSharper, но на самом деле они не существуют во время выполнения.

Если ваши функции принимают свои аргументы в виде кортежа, например:

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"
Другие вопросы по тегам