2012-11-02 9 views
17

Jestem początkującym Haskellem, dopiero zaczynam owijać głowę wokół Monad, ale tak naprawdę to jeszcze nie rozumiem. Piszę grę, która polega na pytaniu użytkownika o dane wejściowe i odpowiedzi. Tutaj jest uproszczoną wersją mojego funkcji:IO dzieje się niesprawna, gdy używa się getLine i putStr

getPoint :: IO Point 
getPoint = do 
    putStr "Enter x: " 
    xStr <- getLine 
    putStr "Enter y: " 
    yStr <- getLine 
    return $ Point (read xStr) (read yStr) 


completeUserTurn :: (Board, Player) -> IO (Board, Player) 
completeUserTurn (board, player) = do 
    putStr $ "Enter some value: " 
    var1 <- getLine 
    putStr $ "Enter another value: " 
    var2 <- getLine 
    putStr $ "Enter a point this time: " 
    point <- getPoint 
    if (... the player entered legal values ...) then do 
     putStr $ "This is what would happen if you did that: {stuff} do you want to do that? (y/n) " 
     continue <- getLine 
     if continue == "y" then 
      return (...updated board..., ...updated player...) 
     else 
      completeUserTurn (board, player) 
    else do 
     putStr "Invalid Move!\n" 
     completeUserTurn (board, player) 

Co się dzieje jest to, że komunikaty będą wyświetlane w porządku z tekstem, który ma się pojawić przed wierszu.

Oto przykład tego, co dzieje się po tym, jak skompilowany kod powyżej:


podać wartość: Wpisz inną wartość:


Wpisz punkt: wpisz x: wprowadź y: y
Czy to prawda? (Y/n).

pogrubiona są rzeczy Wpisałem w

Oczywiście, mam kilka poważnych błędów koncepcyjnych, ale nie wiem co. Zauważ, że działa poprawnie w tłumaczu i kończy się niepowodzeniem podczas kompilacji.

Odpowiedz

37

Jak powiedział Michael, problem jest buforowany. Domyślnie dane wyjściowe są buforowane do momentu wydrukowania nowej linii (lub do momentu, w którym bufor jest pełny, jeśli masz naprawdę długie wiersze), więc najczęściej widzisz ten problem, próbując wyświetlać monity z tej samej linii, używając putStr, tak jak robisz,

Proponuję zdefiniować małego pomocnika działaj tak, aby zadbać o to, aby wykonać płukanie:

import System.IO 

prompt :: String -> IO String 
prompt text = do 
    putStr text 
    hFlush stdout 
    getLine 

Teraz można po prostu zrobić

getPoint = do 
    xStr <- prompt "Enter x: " 
    yStr <- prompt "Enter y: " 
    return $ Point (read xStr) (read yStr) 
+14

To samo zachowanie jak biblioteki C stdio, oprócz tego, że stdio ma hack, który I twierdził Haskell powinien mieć, jak również, a mianowicie jeśli czytać z stdin potem spłukiwania stdout, jeśli oba są tty. – augustss

11

IO dzieje się we właściwej kolejności. Problem polega na buforowaniu. Jeśli opróżnisz standardowe wyjście po każdym putStr, powinno działać tak, jak oczekujesz. Musisz zaimportować hFlush i stdout z System.IO.

10

Problem nie dotyczył kolejności operacji w kodzie IO. Problem polegał na tym, że dane wejściowe i wyjściowe są domyślnie buforowane podczas używania stdin i stdout. Zwiększa to wydajność IO w aplikacji, ale może powodować, że operacje pojawiają się nie w porządku, gdy używane są stdin i stdout.

Są dwa rozwiązania tego problemu. Możesz użyć metody hFlush, aby wymusić przepłukanie klamki (stdin lub stdout). Np. hFlush stdout, hFlush stdin. Prostszym rozwiązaniem (które działa dobrze dla interaktywnych aplikacji) jest całkowite wyłączenie buforowania. Możesz to zrobić, wywołując metody hSetBuffering stdout NoBuffering i hSetBuffering stdin NoBuffering przed uruchomieniem programu (np. Umieść te linie w głównej metodzie)

Powiązane problemy