我在Haskell中有一些对象的列表。我需要找出这些对象中是否有人满足特定条件。因此,我写了以下内容:
any (\x -> check x) xs
但问题是,检查操作非常昂贵,并且列表相当大。我希望看到运行时的当前进度,例如50% (1000/2000 checked).
我怎么能做到这一点?
发布于 2013-10-13 19:33:45
由于您希望查看函数的进度(这是函数的副作用),因此最明显的解决方案是使用monad。因此,首先要做的是创建any
函数的一元版本:
anyM :: (Monad m) => (a -> m Bool) -> [a] -> m Bool
anyM _ [] = return False
anyM pred (x:xs) = reduce (pred x) xs
where reduce acc [] = acc
reduce acc (x:xs) = do
condition <- acc
if condition
then return condition
else reduce (pred x) xs
上面的函数anyM
是any
函数的一元版本。除了检查给定列表中的任何项是否满足给定的谓词之外,它还允许我们产生副作用。
除了执行any
函数之外,我们还可以使用anyM
函数创建另一个函数,该函数显示一个进度条作为副作用,如下所示:
anyVar :: (a -> Bool) -> [a] -> IO Bool
anyVar pred xs = anyM check $ zip [1..] xs
where check (n,x) = do
putStrLn $ show n ++ " checked. "
return $ pred x
请注意,因为我们事先不知道列表的长度,所以我们只显示列表中选中的项数。如果我们事先知道列表中的项目数,那么我们可以显示一个更具信息性的进度条:
anyFix :: (a -> Bool) -> Int -> [a] -> IO Bool
anyFix pred length xs = anyM check $ zip [1..] xs
where check (n,x) = do
putStrLn $ show (100 * n `div` length) ++ "% (" ++
show n ++ "/" ++ show length ++ " checked). "
return $ pred x
对于无限列表和事先不知道长度的列表,可以使用anyVar
函数。对预先知道长度的有限列表使用anyFix
函数。
如果列表很大,并且事先不知道列表的长度,那么length
函数将需要遍历整个列表以确定其长度。因此,使用anyVar
会更好。
最后,总结一下如何使用上面的函数:
main = anyFix (==2000) 2000 [1..2000]
在您的情况下,您可以改为执行以下操作:
main = anyVar check xs
希望这个答案对你有所帮助。
发布于 2013-10-13 17:42:49
最天真和直接的方法是实现你自己的
anyM :: (a -> Bool) -> [a] -> IO Bool
打印进度条(例如,使用terminal-progress-bar)。
但请注意,为了计算百分比,您必须评估完整的列表。这打破了懒惰,并可能对程序的空间行为产生不良和有害的影响。
也有使用unsafePerformIO
和unsafeInterleaveIO
的方法,允许您监视纯计算(如any
),请参阅bytestring-progress的示例。但这是一个可疑的设计,只有当您知道您了解其后果时,才应该使用它。
发布于 2013-10-13 20:52:52
我只需使用Debug.Trace.trace
并跟踪当前位置,如下所示:
any (\(i,x) -> trace (showProgress i (length xs)) $ check x) $ zip [1..] xs
https://stackoverflow.com/questions/19343695
复制相似问题