我在Haskell中列出了一些对象。而且我需要找出这些对象中是否有人满足特定条件。所以,我写了以下内容:

any (\x -> check x) xs

但是问题是检查操作非常昂贵,而且清单很大。我想查看运行时的当前进度,例如50% (1000/2000 checked).我该怎么做?

最佳答案

因为您想查看函数的进度(这是函数的副作用),所以最明显的解决方案是使用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函数的单声道版本。除了检查给定列表中的任何项是否满足给定谓词外,它还可以产生副作用。

除了执行anyM函数外,我们还可以使用any函数创建另一个函数,该函数显示进度条作为副作用。
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/

10-11 22:33
查看更多