Quiero escribir una función que tome un límite de tiempo (en segundos) y una lista, y calcule tantos elementos de la lista como sea posible dentro del límite de tiempo.Calcular la mayor cantidad de una lista posible en un tiempo fijo
Mi primer intento fue escribir primero la función siguiente, que veces un cálculo puro y devuelve el tiempo transcurrido, junto con el resultado:
import Control.DeepSeq
import System.CPUTime
type Time = Double
timed :: (NFData a) => a -> IO (a, Time)
timed x = do t1 <- getCPUTime
r <- return $!! x
t2 <- getCPUTime
let diff = fromIntegral (t2 - t1)/10^12
return (r, diff)
Entonces puede definir la función que quiero en términos de lo siguiente:
timeLimited :: (NFData a) => Time -> [a] -> IO [a]
timeLimited remaining [] = return []
timeLimited remaining (x:xs) = if remaining < 0
then return []
else do
(y,t) <- timed x
ys <- timeLimited (remaining - t) xs
return (y:ys)
Esto no es del todo correcto. Incluso ignorando los errores de sincronización y los errores de punto flotante, este enfoque nunca detiene el cálculo de un elemento de la lista una vez que ha comenzado, lo que significa que puede (y de hecho, normalmente lo hará) superar su límite de tiempo.
Si en lugar de eso tenía una función que podría Evaluación cortocircuito si se hubiera tomado demasiado tiempo:
timeOut :: Time -> a -> IO (Maybe (a,t))
timeOut = undefined
entonces podría escribir la función que realmente quiero:
timeLimited' :: Time -> [a] -> IO [a]
timeLimited' remaining [] = return []
timeLimited' remaining (x:xs) = do
result <- timeOut remaining x
case result of
Nothing -> return []
Just (y,t) -> do
ys <- timeLimited' (remaining - t) xs
return (y:ys)
Mis preguntas son:
- ¿Cómo escribo
timeOut
? - ¿Existe alguna forma mejor de escribir la función
timeLimited
, por ejemplo, una que no tenga un error de punto flotante acumulado al sumar diferencias de tiempo varias veces?
¿No puede ejecutar dos subprocesos en los que un subproceso cuenta hacia atrás el tiempo y elimina el subproceso de cómputo una vez que se ha alcanzado el límite de tiempo? –
Quizás. No he escrito mucho código concurrente en Haskell. ¿Cómo podría devolver la lista parcialmente evaluada? –
Probablemente coloque la lista en un TVar y contra cada nuevo nodo. Acabo de ver que STM.TVar tiene una función llamada 'registerDelay' que también puede ser útil para sincronizar dos hilos. –