我正在尝试在 OCaml 中实现一个通用计时器函数,该函数将任意 arity 的函数作为输入并返回类型 'r 并返回一个函数:
- 输入参数的参数和类型相同和
- 返回类型
float * 'r
其中浮点数将是函数中花费的时间的度量(例如,由Sys.time()
报告)
问题是我无法以它可以处理任何 arity 函数的方式实现它。 例如,以下代码:
让定时器 f = 让计时器 x y = let t0 = 系统时间 () 在 LET 结果 = F x Y in let diff = Sys.time() -.t0 在差异中,结果 在 timerf 中
仅适用于输入参数 2 的函数。对我来说,如何概括它以处理任何 arity 的函数并不明显。我希望部分函数应用程序能以某种方式神奇地解决这个难题,但我无法让它工作。
我理解您打算制作具有任意 arity 的计时器函数的意图。但是你不能在OCaml中以简单的方式做到这一点。
此外,只有一个参数的计时器函数足以在实践中使用:
let timer f x =
let t0 = Sys.time()
in let result = f x
in let diff = Sys.time() -. t0
in diff, result
由于任何具有任何参数的函数g
都可以通过以下方式轻松传递给timer
:
let diff, result = timer (fun () -> g x1 x2 x3 ... xN) ()
或者通过使用部分应用程序(如@Andreas所建议的那样)更好:
let diff, result = timer (g x1 x2 x3 ... xN-1) xN
对 pad 解决方案的评论过于冗长,不适合评论。
在实践中,我发现通过传递()
而不是延迟参数来强制执行f : unit -> 'a
是一种更好的设计。
let timer f =
let t0 = Sys.time() in
let result = f () in
let t1 = Sys.time() in
t1 -. t0, result
原因是我倾向于经常使用以下模式:
let fun_to_test = match some_configuration with ... in
timer fun_to_test
PAD的设计,一开始更吸引人,因为更通用,鼓励你写:
let fun_to_test, arg = match some_configuration with .... in
timer fun_to_test arg
这个选择的问题在于,一开始看起来很好,在添加了一些选项后,你会遇到这样一种情况,即要测试的不同函数的参数不是同一类型。并且您有一个类型错误。错误代码示例:
let fun_to_test, arg =
if Random.bool ()
then (foo, 3)
else (bar, 3.2)
in timer fun_to_test arg
通过强制使用预先传递的参数进行闭包,我在这里免费获得"存在类型":最后一个函数参数的类型不会出现在timer
应用程序的类型中。我发现这在实践中更好。
当然,您也可以延迟完整调用,并在pad的设计中使用()
作为参数。但我更喜欢一个迫使我这样做的选择,因为否则我太想不这样做,我以后再付钱。