2015-09-26 10 views
10

Oft fand ich mich in der Notwendigkeit, den Rest der Iteration (wie continue in C) in Haskell Überspringen:Wie gehe ich in einer Monad-Schleife weiter?

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> 
    ... 

Allerdings habe ich eine einfache Möglichkeit zu finden, nicht zu tun. Die einzige Möglichkeit, die mir bekannt ist, ist wahrscheinlich die Trans.Maybe, aber ist es notwendig, eine Monad-Transformation zu verwenden, um etwas so Triviales zu erreichen?

+2

ein paar Pakete auf Hackage bietet Schleifen verschiedener Art sind. Die interessanteste, die ich gesehen habe, ist wahrscheinlich https://hackage.haskell.org/package/loops – dfeuer

+3

Monade Transformatoren sind nie notwendig - nur praktisch. –

Antwort

13

Denken Sie daran, dass Schleifen wie diese in Haskell keine Magie sind ... sie sind nur normale erstklassige Dinge, die Sie selbst schreiben können.

Für was es wert ist, glaube ich nicht, dass es zu nützlich ist, an MaybeT als ein Monad-Transformator zu denken. Für mich ist MaybeT nur newtype Wrapper eine alternative Implementierung von (>>=) zu geben ... wie, wie Sie verwenden Product, Sum, First, And usw. alternative Implementierungen von mappend und mempty zu geben.

Jetzt, (>>=) für Sie ist IO a -> (a -> IO b) -> IO b. Aber es wäre nützlicher, (>>=) hier IO (Maybe a) -> (a -> IO (Maybe b) -> IO (Maybe b) zu haben. Sobald Sie die erste Aktion erhalten, die eine Nothing zurückgibt, ist es wirklich unmöglich, weiter zu "binden". Genau das gibt Ihnen MaybeT. Sie erhalten auch eine "benutzerdefinierte Instanz" von guard, guard :: Bool -> IO (Maybe a) anstelle von guard :: IO a.

forM_ [1..100] $ \i -> runMaybeT $ do 
    a <- lift doSomeIO 
    guard (isValid1 a) 
    b <- lift $ doSomeOtherIO a 
    guard (isValid2 b) 
    ... 

und das ist es

MaybeT :) ist entweder keine Magie, und Sie können mithilfe von verschachtelten when s im Grunde die gleiche Wirkung erzielen. Es ist nicht notwendig, macht es einfach viel einfacher und sauberer :)

+0

Danke für die tolle Antwort. Aber vielleicht habe ich es nicht klar gemacht (ich bearbeite meine Frage), aber was ich wollte, ist kein "Break", sondern ein "Continue". Es gibt einen subtilen Unterschied --- ich möchte den Rest der * Iteration * überspringen, nicht die gesamte Schleife. Ich habe die Frage geändert. – trVoldemort

2

Wenn Sie über eine Liste oder einen anderen Container, um Aktionen auszuführen und/oder erstellen Sie einen Summenwert Schleife, und Sie finden die üblichen Convenience Tools wie for_ und foldM sind nicht gut genug für den Job, möchten Sie vielleicht foldr betrachten, die viel stark genug für den Job ist. Wenn Sie nicht wirklich über einen Container schleifen, können Sie eine einfache alte Rekursion verwenden oder etwas wie https://hackage.haskell.org/package/loops oder (für einen ganz anderen Geschmack) https://hackage.haskell.org/package/machines oder vielleicht https://hackage.haskell.org/package/pipes einfügen.

+0

Danke. Ich probiere "Loops" aus, aber ich konnte 'break_' nicht zur Arbeit bringen. Können Sie sich [diese Frage] ansehen (http://stackoverflow.com/questions/32918416/how-doi-i-user-break-in-the-package-loops)? – trVoldemort

5

Hier ist, wie Sie es nackten Knochen Rekursion tun würde:

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 

und rufen Sie es dann mit:

loop [1..100] 

Sie können diese ordentlich ein wenig mit dem when Funktion von Control auf. Monad:

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

Es gibt auch unless in Control.Mona d, die Sie vielleicht bevorzugen.

Mit @ Ørjan Johansen ‚s hilfreichen Ratschlägen, hier ist ein einfaches Beispiel:

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] 
Verwandte Themen