我正在尝试在OCaml中实现一个通用计时器函数,它将输入任意arity的函数并返回类型 'r 并返回一个函数: / p>
float * 'r
,其中浮点数是函数花费时间的度量标准(例如Sys.time()
报告)问题是我无法以能够处理任何arity函数的方式实现它。例如。以下代码:
let timer f = let timerf x y = let t0 = Sys.time () in let result = f x y in let diff = Sys.time() -. t0 in diff, result in timerf
仅适用于输入arity 2的功能。对于我来说,如何将其概括为处理任何arity的函数并不明显。我希望部分功能应用程序能以某种方式神奇地解决这个难题,但我无法让它发挥作用。
答案 0 :(得分:9)
我理解你打算用任意arity制作一个计时器功能。但是你不能在OCaml中以一种简单的方式做到这一点。
此外,只有一个参数的计时器功能足以在实践中使用:
let timer f x =
let t0 = Sys.time()
in let result = f x
in let diff = Sys.time() -. t0
in diff, result
由于任何具有任何arity的函数g
都可以通过以下方式轻松传递给timer
:
let diff, result = timer (fun () -> g x1 x2 x3 ... xN) ()
使用部分应用程序或更好(如@Andreas所建议):
let diff, result = timer (g x1 x2 x3 ... xN-1) xN
答案 1 :(得分:7)
对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的设计中使用()
作为参数。但是我更喜欢一种迫使我这样做的选择,因为否则我太诱惑不去做,而且我以后付钱。