2010-12-09 17 views
4

mein Ziel ist es, Haskell-Funktion zu schreiben, die liest N Zeilen aus der Eingabe und verbindet sie in einer Zeichenfolge. Im Folgenden ist der erste Versuch:Haskell Weg zu [IO String] in IO String

readNLines :: Int -> IO String 
readNLines n = do 
    let rows = replicate n getLine 
    let rowsAsString = foldl ++ [] rows 
    return rowsAsString 

Hier Haskell Beschwerden über foldl:

nicht Art erwartet konnte übereinstimmen [a]' against inferred type (a1 -> b -> a1) -> a1 -> [b] -> a1'

wie ich Art von Zeilen verstehen ist [IO String], ist es möglich, einige, wie kommen solche Liste in einem einzigen IO String?

Antwort

6

Außer dem, was ephemient weist darauf hin, ich glaube, Sie einen Syntax Problem haben: Die Art und Weise Sie die ++ Operator verwenden sie aussehen lässt wie Sie versuchen, die aufzurufen ++ Operator mit Operanden foldl und []. Setzen Sie den ++ Operator in Klammern Ihre Absicht klar zu machen:

foldl (++) [] rows 
+0

Ah ja, das ist die unmittelbare Ursache für den Tycheck-Fehler ... den ich ignoriert habe, denn selbst nach OP-Korrekturen haben sie noch ein anderes Problem. – ephemient

+0

Sollte auch beachten, dass 'foldl (++) []' ist das gleiche wie 'concat'. – HaskellElephant

5

Die Funktionen Sie suchen, ist sequence ist jedoch darauf hinzuweisen, dass

sequence (replicate n f) 

die gleiche ist wie

replicateM n f 

Und foldl (++) [] entspricht concat. So Ihre Funktion ist:

readNLines n = liftM concat (replicateM n getLine) 

Alternativ, wenn Sie Zeilenumbrüche erhalten wollen:

readNLines n = liftM unlines (replicateM n getLine) 
+0

'concat' ist' foldr (++) [] ', nicht' foldl': funktioniert gut bei unendlichen Streams von Listen. Sie vermissen auch 'n' als Argument für' replicateM' in Ihren späteren Beispielen. Aber ja, das sind gute Punkte. – ephemient

+0

@ephemient, danke für die Köpfe, ich habe den Fehler mit n behoben, und ja 'concat' ist wirklich' foldr (++) [] ', was ich meine, war, dass er' foldl (++) [] 'geschrieben hat das ist gleichbedeutend mit "concat", obwohl, wie Sie erwähnen, "concat" eigentlich besser ist, da es auf unendlichen Streams arbeitet. – HaskellElephant

0

replicate eine Liste von IO String Aktionen zurückgibt. Um diese Aktionen auszuführen, müssen sie in der IO-Monade ausgeführt werden. Sie möchten also nicht einem Array von IO-Aktionen beitreten, sondern alle nacheinander ausführen und das Ergebnis zurückgeben.

Hier ist, was ich

readNLines :: Int -> IO String 
readNLines n = do 
    lines <- replicateM n getLine 
    return $ concat lines 

tun würde Oder in applicative Stil:

import Control.Applicative 

readNLines :: Int -> IO String 
readNLines n = concat <$> replicateM n getLine 

Beide verwenden die monadische Replikation (replicateM), die eine Liste von monadischen Werte nacheinander auswertet, anstatt einfach eine Liste der Aktionen zurück zu senden

1

Die kürzeste Antwort, die ich mir vorstellen kann, ist:

import Control.Applicative 
import Control.Monad 

readNLines :: Int -> IO String 
readNLines n = concat <$> replicateM n getLine