2015-09-26 5 views
10

しばしば私はHaskellの中反復(Cのようにcontinue)の残りの部分をスキップする必要性に自分自身を発見した:`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ですが、それほど簡単なものを実現するためにはモナド変換を使う必要がありますか?

+2

:Ørjan・ヨハンセンの有益な助言@使用

は、ここに簡単な例です。私が見た中で最も興味深いのはおそらくhttps://hackage.haskell.org/package/loops – dfeuer

+3

です。モナド・トランスは決して便利ではありません。 –

答えて

13

Haskellで、このようにループすることを覚えておくが、彼らはあなた自身を書くことができます普通の第一級のものだ...魔法ではありません。

何が価値があるため、私はそれはモナド変換子としてMaybeTを考えるのは、あまりにも便利だとは思いません。私には、MaybeTmappendmemptyの代替実装を提供するなど、ちょうどあなたがProductSumFirstをどのように使用するかのように... And(>>=)の代替実装を与えるためだけのnewtypeラッパーです。

は今、あなたのための(>>=)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 Sを使って、基本的に同じ効果を得ることができます。 は必要ありません、それは物事をより簡単できれいにするだけです:)

+0

偉大な答えをありがとう。しかし、多分私はそれを明確にしなかったかもしれませんが(私の質問を編集しています)、私が望んでいたのは「休憩」ではなく「続ける」ことでした。微妙な違いがあります---ループ全体ではなく、残りの*繰り返しをスキップしたいと思います。私は質問を変更しました。 – trVoldemort

2

リストや他のコンテナをループしてアクションを実行したり、要約値を生成したりする場合は、通常のfor_foldMなどの便利なツールは、仕事のために十分ではありません、あなたは仕事のために十分に十分に強いである、foldrを検討する必要があります。あなたが本当にコンテナをループしていないときは、昔ながらの再帰を使用するか、またはhttps://hackage.haskell.org/package/loops(非常に異なる味のために)https://hackage.haskell.org/package/machinesまたは多分https://hackage.haskell.org/package/pipesのようなもので引っ張ることができます。

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 

をし、その後でそれを呼び出します:ここで

+0

ありがとうございます。私は 'ループ 'を試していますが、' break_'を働かせることはできませんでした。あなたは[この質問](http://stackoverflow.com/questions/32918416/how-do-i-use-break-in-the-package-loops)を見ることができますか? – trVoldemort

5

は、あなたが最低限の再帰を使用してそれを行うだろうかだ

loop [1..100] 

あなたがコントロールからwhen機能でビットを、これを整理することができます。モナド:

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

Control.Monaにはunlessもありますあなたが好むかもしれないd。様々な種類のループを提供Hackageには、いくつかのパッケージがあります

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] 
関連する問題