我经常发现自己需要在Haskell中跳过剩余的迭代(如C中的continue
):
forM_ [1..100] $ \ i ->
a <- doSomeIO
when (not $ isValid1 a) <skip_rest_of_the_iteration>
b <- doSomeOtherIO a
when (not $ isValid2 b) <skip_rest_of_the_iteration>
...
然而,我没有找到一个简单的方法。我所知道的唯一方法可能是Trans.Maybe
,但是有必要使用monad变换来实现如此微不足道的事情吗?
答案 0 :(得分:13)
请记住,在Haskell中这样的循环并不神奇......它们只是你自己写的正常的一流的东西。
对于它的价值,我认为将MaybeT
视为Monad变换器并不太有用。对我而言,MaybeT
只是一个新类型的包装器,可以提供(>>=)
的替代实现...就像您使用Product
,Sum
,First
一样, And
等,以提供mappend
和mempty
的替代实施。
目前,(>>=)
对你来说是IO a -> (a -> IO b) -> IO b
。但是(>>=)
这里IO (Maybe a) -> (a -> IO (Maybe b) -> IO (Maybe b)
更为有用。一旦你得到第一个返回Nothing
的动作,就不可能再“绑定”了。这正是MaybeT
给你的。您还会获得guard
,guard :: Bool -> IO (Maybe a)
的“自定义实例”,而不是guard :: IO a
。
forM_ [1..100] $ \i -> runMaybeT $ do
a <- lift doSomeIO
guard (isValid1 a)
b <- lift $ doSomeOtherIO a
guard (isValid2 b)
...
就是这样:))
MaybeT
也不是魔术,你可以通过使用嵌套的when
来达到基本相同的效果。它不是必需的,它只会使事情变得更简单和清洁:)
答案 1 :(得分:5)
以下是使用简单递归的方法:
loop [] = return () -- done with the loop
loop (x:xs) =
do a <- doSomeIO
if ...a...
then return () -- exit the loop
else do -- continuing with the loop
b <- doSomeMoreIO
if ...b...
then return () -- exit the loop
else do -- continuing with the loop
...
loop xs -- perform the next iteration
然后用:
调用它loop [1..100]
您可以使用Control.Monad中的when
函数稍微整理一下:
loop [] = return ()
loop (x:xs) =
do a <- doSomeIO
when (not ...a...) $ do
b <- doSomeMoreIO
when (not ...b...) $ do
...
loop xs
您可能更喜欢使用Control.Monad中的unless
。
使用@ØrjanJohansen的有用建议,这是一个简单的例子:
import Control.Monad
loop [] = return ()
loop (x:xs) = do
putStrLn $ "x = " ++ show x
a <- getLine
when (a /= "stop") $ do
b <- getLine
when (b /= "stop") $ do
print $ "iteration: " ++ show x ++ ": a = " ++ a ++ " b = " ++ b
loop xs
main = loop [1..3]
答案 2 :(得分:2)
如果您想循环遍历列表或其他容器以执行操作和/或生成汇总值,并且您发现for_
和foldM
这样的常用便利工具并不好对于这项工作而言,你可能想要考虑foldr
,这对于这项工作来说足够强大。当你没有真正地循环容器时,你可以使用普通的旧递归或拉出https://hackage.haskell.org/package/loops
或(对于一种非常不同的味道)https://hackage.haskell.org/package/machines
或者https://hackage.haskell.org/package/pipes
。