2017-11-06 2 views
4

Ich möchte eine Liste der Strings lesen, die durch Zeilenumbrüche von STDIN getrennt sind, bis eine neue Zeile zu sehen ist und ich eine Aktion vom Typ IO [String] möchte. Hier ist, wie ich es mit Rekursion tun würde:Der Haskell-Weg, IO-Loops (ohne explizite Rekursion) zu machen?

myReadList :: IO String 
myReadList = go [] 
where 
    go :: [String] -> IO [String] 
    go l = do { 
       inp <- getLine; 
       if (inp == "") then 
        return l; 
       else go (inp:l); 
       } 

Dieses Verfahren ist jedoch von unterwegs mit verschleierter Lesbarkeit und ist ein Muster, so verbreitet, dass man idealerweise zu abstrahieren diesen wollen würde.

So, das war mein Versuch:

whileM :: (Monad m) => (a -> Bool) -> [m a] -> m [a] 
whileM p []  = return [] 
whileM p (x:xs) = do 
    s <- x 
    if p s 
    then do 
     l <- whileM p xs 
     return (s:l) 
    else 
     return [] 

myReadList :: IO [String] 
myReadList = whileM (/= "") (repeat getLine) 

ich dort bin zu raten, ist einige Standardimplementierung dieser whileM oder etwas ähnliches bereits. Aber ich kann es nicht finden.

Könnte jemand darauf hinweisen, was der natürlichste und eleganteste Weg ist, um mit diesem Problem umzugehen?

+0

whileM ist in Control.Monad.Loops. –

+0

@ n.m. Ich weiß, aber es macht etwas anderes. Es hat auch eine andere Signatur: 'Monad m => m Bool -> ma -> m [a]' –

+0

Ihre Version scheint nahe zu entfalten, aber Sie nehmen eine Liste von monadischen Aktionen, die Sie nicht wirklich brauche (du generierst die Liste trotzdem mit repeat). –

Antwort

12

unfoldWhileM ist das gleiche wie Ihre whileM, außer dass es eine Aktion (keine Liste) als zweites Argument benötigt.

myReadList = unfoldWhileM (/= "") getLine 
1

Ja zum Abstrahieren die explizite Rekursion wie in der vorherigen Antwort erwähnt, gibt es die Control.Monad.Loop Bibliothek, die nützlich ist. Für diejenigen, die interessiert sind here is a nice tutorial on Monad Loops.

Allerdings gibt es einen anderen Weg. Zuvor, mit diesem Job zu kämpfen und zu wissen, dass Haskell standardmäßig Lazy ist, habe ich zuerst versucht;

(sequence . repeat $ getLine) >>= return . takeWhile (/="q") 

erwartete ich die oben angegebenen Zeilen in eine IO [String] Art zu sammeln. Nein ... Es läuft unbegrenzt und IO Actişons sehen überhaupt nicht faul aus. An dieser Stelle könnte auch System IO Lazy nützlich sein. Es ist eine einfache Bibliothek mit nur 2 Funktionen.

run  :: T a -> IO a 
interleave :: IO a -> T a 

So run nimmt eine faule IO Aktion und wandelt es in ein IO-Aktion und interleave tut das Gegenteil. Dementsprechend, wenn wir die obige Funktion als umformulieren;

import qualified System.IO.Lazy as LIO 

gls = LIO.run (sequence . repeat $ LIO.interleave getLine) >>= return . takeWhile (/="q") 

Prelude> gls >>= return . sum . fmap (read :: String -> Int) 
1 
2 
3 
4 
q 
10 
1

Eine Lösung mit der effektStröme des streaming Paket mit:

import Streaming 
import qualified Streaming.Prelude as S 

main :: IO() 
main = do 
    result <- S.toList_ . S.takeWhile (/="") . S.repeatM $ getLine 
    print result 

eine Lösung, die Eingabeaufforderungen, halten sie von der Leseaktionen getrennt zeigt:

main :: IO() 
main = do 
    result <- S.toList_ 
      $ S.zipWith (\_ s -> s) 
         (S.repeatM $ putStrLn "Write something: ") 
         (S.takeWhile (/="") . S.repeatM $ getLine) 
    print result 
+0

Das ist auch cool ... – Redu

Verwandte Themen