如何在"Monad"循环中"继续"?

xzh*_*zhu 10 haskell

通常我发现自己需要在Haskell 中跳过迭代的其余部分(比如continue在C中):

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>
    ...
Run Code Online (Sandbox Code Playgroud)

但是,我没有找到一个简单的方法.我所知道的唯一方法可能是Trans.Maybe,但是有必要使用monad变换来实现如此微不足道的东西吗?

Jus*_* L. 13

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

对于它的价值,我认为将MaybeTMonad变换器视为Monad变压器并不太有用.对我来说,MaybeT只是一个NEWTYPE包装给替代实现的(>>=)...就像你如何使用Product,Sum,First,And,等方面给予的替代实现mappendmempty.

现在,(>>=)对你而言IO a -> (a -> IO b) -> IO b.但它会是有更多的有用(>>=)这里是IO (Maybe a) -> (a -> IO (Maybe b) -> IO (Maybe b).一旦你进入第一个返回a的动作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)
  ...
Run Code Online (Sandbox Code Playgroud)

就是这样:)

MaybeT也不是魔术,你可以通过使用嵌套的whens 来达到基本相同的效果.这没有必要,只是让事情变得更简单,更清洁:)


Eri*_*ikR 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
Run Code Online (Sandbox Code Playgroud)

然后使用以下命令调用它:

loop [1..100]
Run Code Online (Sandbox Code Playgroud)

你可以使用whenControl.Monad中的函数整理一下这个:

    loop [] = return ()
    loop (x:xs) =
      do a <- doSomeIO
         when (not ...a...) $ do
           b <- doSomeMoreIO
           when (not ...b...) $ do
             ...
             loop xs
Run Code Online (Sandbox Code Playgroud)

还有unless在Control.Monad,你可能更愿意使用.

使用@Ø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]
Run Code Online (Sandbox Code Playgroud)