2012-04-04 17 views
7

Estoy tratando de implementar una función de temporizador genérica en OCaml que tendrá como entrada una función de aridad arbitraria y tipo 'r volver y devuelve una función con:temporizador genérica en OCaml

  • la misma aridad y los tipos de parámetros de entrada , y
  • retorno tipo float * 'r donde el flotador sería una métrica del tiempo pasado en la función (por ejemplo reportado por Sys.time())

El problema es que no puedo implementarlo de forma que pueda manejar funciones de cualquier tipo. P.ej. el siguiente código:

 
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  

funciona sólo con funciones de aridad de entrada 2. No es obvio para mí cómo generalizar que para manejar las funciones de cualquier aridad. Esperaba que las aplicaciones de función parcial de alguna manera mágicamente resolverían el enigma, pero no puedo hacerlo funcionar.

Respuesta

9

Entiendo su intención de realizar una función de temporizador con aria arbitraria. Pero no puedes hacerlo de manera fácil en OCaml.

Por otra parte, una función de temporizador con un solo parámetro es suficiente para su uso en la práctica:

let timer f x = 
    let t0 = Sys.time()           
    in let result = f x            
    in let diff = Sys.time() -. t0          
    in diff, result 

Desde cualquier función g con cualquier aridad se puede pasar a timer fácilmente por:

let diff, result = timer (fun() -> g x1 x2 x3 ... xN)() 

o mejor utilizando la aplicación parcial (como lo sugiere @Andreas):

let diff, result = timer (g x1 x2 x3 ... xN-1) xN 
+0

Si sabes que la función no hace nada interesante cuando se aplica parcialmente (como la mayoría de las funciones), entonces ni siquiera necesitas la abstracción. Es suficiente llamar 'timer (g x1 x2 ... xN-1) xN'. –

+0

@AndreasRossberg: Gracias, incorporé su punto en mi respuesta. – pad

7

Un comentario sobre la solución de pad que fue demasiado detallado para caber un comentario.

En la práctica, encontré que era un mejor diseño para aplicar f : unit -> 'a pasando () en lugar de un argumento retrasado.

let timer f = 
    let t0 = Sys.time() in 
    let result = f() in 
    let t1 = Sys.time() in 
    t1 -. t0, result 

La razón por la cual es que tiendo a utilizar el siguiente patrón muy a menudo:

let fun_to_test = match some_configuration with ... in 
timer fun_to_test 

diseño de la almohadilla, lo que es más atractivo al principio, porque más general, alienta a que en lugar de escribir:

let fun_to_test, arg = match some_configuration with .... in 
timer fun_to_test arg 

El problema con esta opción es que parece estar bien al principio, y después de agregar algunas opciones se encuentra con un caso donde los argumentos para las diferentes funciones para probar no son del ame tipo Y tienes un error de tipo. Ejemplo de mal código:

let fun_to_test, arg = 
    if Random.bool() 
    then (foo, 3) 
    else (bar, 3.2) 
in timer fun_to_test arg 

Al forzar un cierre con parámetros pre-aprobada, consigo "un tipo existencial" de aquí libremente: el tipo del último argumento de la función no aparece en el tipo de timer solicitud. Encuentro que esto es mejor en la práctica.

Por supuesto, también puede retrasar la llamada completa y usar () como argumento en el diseño del panel. Pero prefiero una opción que me obligue a hacerlo, porque de lo contrario estoy demasiado tentado de no hacerlo, y lo pagaré más adelante.

+0

mi experiencia segundos esto – ygrek