我想编写一个函数,它需要一个时间限制(以秒为单位)和一个列表,并在时间限制内计算尽可能多的列表元素。
我的第一次尝试是首先编写以下函数,该函数对纯计算进行计时并返回经过的时间以及结果:
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
,例如,不会因多次累加时间差而遭受累积浮点误差的影响?