2015-09-26 81 views
10

很多時候,我發現自己需要在Haskell跳過迭代的(如continue在C),其餘的:如何在「Monad」循環中「繼續」?

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變換來實現如此微不足道的事情嗎?

+2

上有Hackage提供各種種類的循環了幾包。我見過的最有趣的可能是https://hackage.haskell.org/package/loops – dfeuer

+3

Monad變壓器從來沒有必要 - 只是方便。 –

回答

13

請記住,在Haskell中這樣的循環不是魔術......它們只是您可以自己編寫的普通的一流的東西。

對於它的價值,我認爲MaybeT作爲Monad變壓器不是太有用。對我來說,MaybeT只是NEWTYPE包裝給替代實施(>>=) ...就像你如何使用ProductSumFirstAnd等方面給予的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的基本屬於達到同樣的效果。這是沒有必要,它只是讓事情更簡單和更清潔:)

+0

感謝您的好評。然而,也許我沒有說清楚(我正在編輯我的問題),但我想要的不是「休息」而是「繼續」。有一個細微的差別---我想跳過* iteration *的其餘部分,而不是整個循環。我改變了這個問題。 – trVoldemort

2

如果你想循環一個列表或其他容器來執行操作和/或產生一個彙總值,你會發現通常諸如for_foldM等便利工具對於這項工作來說還不夠好,您可能需要考慮foldr,這對於工作來說足夠強大。當你沒有真正地循環一個容器時,你可以使用普通的舊遞歸或拉入類似https://hackage.haskell.org/package/loops或(對於非常不同的風味)https://hackage.haskell.org/package/machines或者可能https://hackage.haskell.org/package/pipes

+0

謝謝。我嘗試'循環',但我不能'break_'工作。你可以看看[這個問題](http://stackoverflow.com/questions/32918416/how-do-i-use-break-in-the-package-loops)? – trVoldemort

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] 

您可以用控制的when功能有點整理這件事。 Monad:

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

Control.Mona中還有unless d您可能更喜歡使用。

使用@與Orjan約翰森的有用的建議,這裏是一個簡單的例子:

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]