2016-12-19 8 views
1

Ich habe ein kleines interaktives Spieleprogramm. Der interaktive Teil sieht so aus:IO in Haskell nicht sichtbar

main :: IO() 
main = playGame newGame 
    where 

    playGame :: Game -> IO() 
    playGame game = 
    do putStr $ show game 
     putStr $ if gameOver game then "Another game? (y or n) > " 
           else show (whoseMove game) ++ " to play (row col) > " 
     moveWords <- fmap (words . fmap cleanChar) getLine 
     if stopGame game moveWords 
     then return() 
     else playGame $ if gameOver game then newGame else makeMove game moveWords 

Das funktioniert gut. Es zeigt den Spielzustand, fragt nach dem nächsten Schritt gilt diese Bewegung in den Zustand, zeigt den neuen Zustand usw.

Dann sah ich ein video von Moss Collum in dem er ein game zeigte, dass die folgende Strategie verwendet für die Interaktion mit dem Benutzer.

... 
userInput <- getContents 
foldM_ updateScreen (12, 40) (parseInput userInput) where 
... 

Ich konnte kein Hinweis auf foldM_ finden aber es war eine Art von fold ich versuchte, unter der Annahme. (Ich habe versucht, tatsächlich eine Reihe von Dingen, aber dies scheint klarsten.)

main' :: IO() 
main' = do 
    moveList <- fmap (map words . lines . map cleanChar) getContents 
    let states = scanl makeMove newGame moveList 
    foldl (\_ state -> putStr . show $ state) (return()) states 

Wenn ich es laufen, habe ich nie das Spiel Zustand erhalten ausgedruckt, bis nach der Kollision mit End-of-Datei, an welchem ​​Punkt die korrekte endgültige Der Spielstatus wird gedruckt. Davor kann ich Moves eingeben und sie werden richtig verarbeitet (entsprechend dem finalen Spielzustand), aber ich sehe nie die Zwischenzustände. (Die Idee ist, dass eine faule Auswertung die Spielstände drucken sollte, sobald sie verfügbar sind.)

Ich würde gerne verstehen, warum ich die Zwischenzustände nicht sehe und was ich tun kann, um es zu reparieren.

Nach der Eingabe des Dateiendes (^ Z unter Windows) verweigert das Programm die Wiedergabe und sagt, dass das Handle geschlossen wurde. Um wieder zu spielen, muss ich das Programm neu starten. Gibt es eine Möglichkeit, das zu beheben?

+3

Sie falten keine Sequenzeffekte (grob, das M in 'foldM'). Wenn du 'foldl' verwenden willst, probiere zumindest etwas wie' \ act state -> act >> putStr .... 'so dass der vorherige' act' tatsächlich ausgeführt wird. Ich würde auch versuchen, 'mapM',' forM' oder ihre Anwendung Cousins ​​zu verwenden. Z.B. Versuchen Sie 'for_ [1..10] print' nach dem Import' Data.Foldable' – chi

+0

Überprüfen Sie die Haskell-Tag über Abschnitt für Informationen über hoogle und andere Goodies – jberryman

+0

Danke. forM_ erledigt den Job. Gibt es eine Möglichkeit, nach der Eingabe des Dateiendes erneut zu öffnen? – RussAbbott

Antwort

0

Lassen Sie uns zunächst mit foldM starten:

foldM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b 

a ist die Art von Dingen in unserem Container und b ist unser Ergebnistyp. Es dauert einen b (den Wert, den wir , an(then ext thing in the list ein bisher in unserer Falte angesammelt haben, und gibt eine m b, dh es ist ein b gibt und tut einige monadischen Aktionen in der Monade m.

Dann ist es einen Anfangswert nimmt , ein Container von a, und es gibt einen

Dies ist im Grunde wie eine normale Faltung Funktion, aber jeder Schritt der Falte ist monadisch, und das Endergebnis ist monadisch.So die Aktionen werden tatsächlich durchgeführt, wenn Sie Verwenden Sie foldM.

Nun schauen wir uns foldM_:

foldM_ :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m() 

Quelle

Dies ist die gleiche, aber das Endergebnis ist m(). Dies ist für den Fall, dass uns das Endergebnis egal ist. Wir behalten die Zwischenergebnisse und verwenden sie, um bei jedem Schritt monadische Aktionen durchzuführen, aber wenn wir fertig sind, kümmern wir uns nur darum, was wir in der Monade gemacht haben, also werfen wir das Ergebnis weg.

In Ihrem Fall wäre tList, und m wäre IO. So iteriert foldM_ durch die Liste, führt die ausgewählten IO-Aktionen in jeder Phase aus und verwirft dann das Endergebnis.

fold führt die Aktionen nicht zusammen, sondern faltet die Liste normalerweise zusammen, auch wenn das Endergebnis IO ist. So erstellt Ihr foldl eine IO-Aktion, nämlich putStr . show $ state, dann geht es an den nächsten Schritt der Faltung. Aber, du ignorierst das erste Argument deiner Falte, so dass es das IO wegwirft, ohne jemals irgendetwas damit zu tun!

Dies ist eine schwierige Sache in Haskell. Ein Wert vom Typ IO Something führt die Aktion beim Erstellen nicht durch. Es erstellt nur einen IO Wert, der nur eine Anweisung an die Laufzeit ist, was zu tun ist, wenn es main ausführt. Wenn Sie es wegwerfen, und es wird nie in eine IO sequenziert, die Ihr Haupt durchführt, dann wird der Nebeneffekt nie passieren.