如何在“Monad”循环中“继续”?

时间:2015-09-26 02:45:49

标签: haskell

我经常发现自己需要在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变换来实现如此微不足道的事情吗?

3 个答案:

答案 0 :(得分:13)

请记住,在Haskell中这样的循环并不神奇......它们只是你自己写的正常的一流的东西。

对于它的价值,我认为将MaybeT视为Monad变换器并不太有用。对我而言,MaybeT只是一个新类型的包装器,可以提供(>>=)的替代实现...就像您使用ProductSumFirst一样, And等,以提供mappendmempty的替代实施。

目前,(>>=)对你来说是IO a -> (a -> IO b) -> IO b。但是(>>=)这里IO (Maybe a) -> (a -> IO (Maybe b) -> IO (Maybe b)更为有用。一旦你得到第一个返回Nothing的动作,就不可能再“绑定”了。这正是MaybeT给你的。您还会获得guardguard :: 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