Передайте функцию в качестве параметра и перегрузите ее
Я хочу определить время моих функций, некоторые из них используют до трех параметров. Прямо сейчас я использую тот же код ниже с некоторыми вариациями для трех.
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"