Я хочу написать функцию, которая принимает ограничение по времени (в секундах )и список, и вычисляет как можно больше элементов списка в течение срока.
Моя первая попытка состояла в том, чтобы сначала написать следующую функцию, которая измеряет чистое вычисление и возвращает прошедшее время вместе с результатом:
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)
Затем я могу определить нужную мне функцию с точки зрения этого:
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)
Хотя это не совсем правильно. Даже игнорируя ошибки синхронизации и ошибки с плавающей запятой, этот подход никогда не останавливает вычисление элемента списка после его запуска, а это означает, что он может (и фактически )превысит свой лимит времени.
Если бы вместо этого у меня была функция, которая могла бы закорачивать -оценку цепи, если бы это заняло слишком много времени:
timeOut :: Time -> a -> IO (Maybe (a,t))
timeOut = undefined
тогда я мог бы написать функцию, которую я действительно хочу:
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)
Мои вопросы:
timeOut
?timeLimited
, например, такую, которая не страдает от накопленной ошибки с плавающей запятой из-за многократного суммирования разницы во времени?