Oblicz jak najwięcej listy w ustalonym czasie
Chcę napisać funkcję, która zajmuje limit czasu (w sekundach) i listę, i oblicza tyle elementów listy, ile to możliwe w określonym czasie.
Moją pierwszą próbą było najpierw napisanie następującej funkcji, która jest czasem obliczeń czystych i zwraca czas, który upłynął wraz z wynikiem:
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)
Mogę wtedy zdefiniować funkcję, którą chcę:
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)
To jednak nie do końca prawda. Nawet ignorując błędy czasowe i błędy zmiennoprzecinkowe, takie podejście nigdy nie zatrzymuje obliczania elementu listy po jego uruchomieniu, co oznacza, że może (a właściwie normalnie) przekroczy swój limit czasowy.
Gdybym zamiast tego miał funkcję, która mogłaby spowodować zwarcie oceny, gdyby trwała zbyt długo:
timeOut :: Time -> a -> IO (Maybe (a,t))
timeOut = undefined
wtedy mógłbym napisać funkcję, którą janaprawdę chcieć:
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)
Moje pytania to:
Jak pisaćtimeOut
?Czy jest lepszy sposób na napisanie funkcjitimeLimited
na przykład taki, który nie ma skumulowanego błędu zmiennoprzecinkowego z wielokrotnego dodawania różnic czasowych?