Haskell基准测试中传递随机生成列表参数报错求助
解决Haskell Criterion基准测试中的IO类型不匹配问题
嘿,这个问题其实是因为你混淆了Criterion中纯函数基准测试和IO操作基准测试的用法,我来给你一步步理清楚:
错误原因分析
你看报错信息里说的:Couldn't match expected type ‘[a0]’ with actual type ‘IO [t0]’,问题出在这一行:
bench "[1,2,3]" $ whnf printElements (randomList 10)
whnf是Criterion用来测试纯函数的工具:它需要你传入一个纯函数(这里是printElements,类型是Show a => [a] -> IO ())和一个纯输入值(也就是[a]类型的列表)。- 但
randomList 10返回的是IO [Int]——这是一个IO动作,不是纯列表,所以类型完全不匹配,自然报错了。
另外还要注意:printElements本身虽然接受纯列表,但它返回的是IO (),所以如果你只是用whnf测试printElements someList,其实测的是"生成IO动作"的耗时,而不是执行打印操作的耗时——这几乎没意义,因为真正耗时的是打印本身。
两种解决方案
方案1:测试"生成随机列表+打印"的整体IO耗时
如果你想测的是从生成随机数到打印完成的完整流程,应该把这两个步骤合并成一个IO动作,然后用Criterion专门测试IO的函数whnfIO或者nfIO:
修改后的完整代码:
{-# LANGUAGE OverloadedStrings #-} import System.Random import Control.Exception import Criterion.Main printElements [] = return () printElements (x:xs) = do print(x) printElements xs randomList 0 = return [] randomList n = do x <- randomRIO (1,100) xs <- randomList (n-1) return (x:xs) main = defaultMain [ bgroup "printElements" [ -- 把生成列表和打印合并成一个IO动作,用whnfIO测试 bench "random-list-10" $ whnfIO (randomList 10 >>= printElements), -- 纯列表的测试也要改成测试IO动作的执行 bench "[4,5,6,...]" $ whnfIO (printElements [4,5,6,4,2,5]), bench "[7,8,9,...]" $ whnfIO (printElements [7,8,9,2,3,4]), bench "[10,11,12,...]" $ whnfIO (printElements [10,11,12,4,5]) ] ]
这里randomList 10 >>= printElements的意思是:先执行randomList 10得到随机列表,再把这个列表传给printElements执行打印,最终得到一个IO ()动作,whnfIO会帮你执行这个动作并测量耗时。
方案2:先固定随机列表,再测试打印纯列表的耗时
如果你想避免每次测试生成不同的随机列表(防止随机数差异影响基准结果),可以先在main里生成一次随机列表,再把这个纯列表传给whnf:
修改后的代码:
{-# LANGUAGE OverloadedStrings #-} import System.Random import Control.Exception import Criterion.Main printElements [] = return () printElements (x:xs) = do print(x) printElements xs randomList 0 = return [] randomList n = do x <- randomRIO (1,100) xs <- randomList (n-1) return (x:xs) main = do -- 先在IO中生成纯随机列表 randList <- randomList 10 defaultMain [ bgroup "printElements" [ -- 现在randList是纯[Int],和whnf的要求匹配 bench "random-list-10" $ whnf printElements randList, bench "[4,5,6,...]" $ whnf printElements [4,5,6,4,2,5], bench "[7,8,9,...]" $ whnf printElements [7,8,9,2,3,4], bench "[10,11,12,...]" $ whnf printElements [10,11,12,4,5] ] ]
注意:这种方案测的是printElements在固定列表上的打印耗时,生成随机列表的时间不会被算进基准测试里。
小提醒
- 如果你主要关心打印操作的性能,方案1更贴合你的需求,因为它包含了生成列表到打印的完整流程。
whnfIO和nfIO的区别:whnfIO只评估到弱头范式(比如对于IO动作,就是执行它),nfIO会完全评估结果,对于IO ()来说两者区别不大,但如果你的IO返回复杂值,可能需要选nfIO。
内容的提问来源于stack exchange,提问作者AR17
相关产品推荐
相关产品推荐

