我在 Haskell 中有一些对象的列表。我需要查明这些对象中是否有人满足某些条件。所以,我写了以下内容:
any (\x -> check x) xs
但问题是检查操作的开销非常大,而且列表也很大。我想查看运行时的当前进度,例如 50%(选中 1000/2000)。
我怎样才能做到这一点?
最佳答案
由于您想查看函数的进度(这是函数的副作用),最明显的解决方案是使用 monad。因此,首先要做的是制作 any
函数的单子(monad)版本:
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
函数的单子(monad)版本。除了检查给定列表中的任何项目是否满足给定谓词之外,它还允许我们产生副作用。
除了执行 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
希望这个回答对你有帮助。
关于haskell - 显示 Haskell 程序的进度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19343695/