我偶尔会想要在开发/测试时延迟纯算法的特定部分,所以我可以通过观察逐个累积的懒惰结果来监视评估(这通常太快而无法在最终使用延迟版).然后我发现自己插入了丑陋的东西,比如sum [1..1000000] `seq` q哪种作品(虽然经常会出现通常的爆炸问题,因为我从来没有想过这个问题),但它更像是试错了.
当我想以这种方式进行一些快速测试并且不能打扰进行适当的分析criterion等时,是否有一个更好,更可控的替代方案仍然同样简单?
我也想避免unsafePerformIO $ threadDelay,但我认为这可能是一个合适的用法.
这个循环解决方案避免了调用threadDelay,但仍然调用unsafePerformIO,所以也许我们没有得到太多:
import Data.AdditiveGroup
import Data.Thyme.Clock
import Data.Thyme.Clock.POSIX
import System.IO.Unsafe
pureWait :: NominalDiffTime -> ()
pureWait time = let tsList = map unsafePerformIO ( repeat getPOSIXTime ) in
case tsList of
(t:ts) -> loop t ts
where
loop t (t':ts') = if (t' ^-^ t) > time
then ()
else loop t ts'
main :: IO ()
main = do
putStrLn . show $ pureWait (fromSeconds 10)
Run Code Online (Sandbox Code Playgroud)
更新:这是一个替代解决方案。首先确定(使用IO)需要多少次迭代才能实现给定的延迟,然后仅使用纯循环函数。
pureWait :: Integer -> Integer
pureWait i = foldl' (+) 0 $ genericTake i $ intersperse (negate 1) (repeat 1)
calibrate :: NominalDiffTime -> IO Integer
calibrate timeSpan = let iterations = iterate (*2) 2 in loop iterations
where
loop (i:is) = do
t1 <- getPOSIXTime
if pureWait i == 0
then do
t2 <- getPOSIXTime
if (t2 ^-^ t1) > timeSpan
then return i
else loop is
else error "should never happen"
main :: IO ()
main = do
requiredIterations <- calibrate (fromSeconds 10)
putStrLn $ "iterations required for delay: " ++ show requiredIterations
putStrLn . show $ pureWait requiredIterations
Run Code Online (Sandbox Code Playgroud)