Подтвердить что ты не робот

IO не работает при использовании getLine и 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)

Что происходит, так это то, что подсказки появятся не в порядке с текстом, который должен появиться перед приглашением.

Вот пример того, что происходит после того, как я скомпилировал приведенный выше код:

1
Введите некоторое значение: введите другое значение: 2
3
4
Введите точку на этот раз: Введите x: Введите y: y
Это верно? (y/n):

Жирным шрифтом являются то, что я набрал.

Очевидно, у меня есть некоторые основные концептуальные ошибки, но я не знаю, что. Обратите внимание, что он корректно работает в интерпретаторе и сбой при компиляции.

4b9b3361

Ответ 1

Как сказал Майкл, проблема буферизации. По умолчанию вывод буферизуется до тех пор, пока вы не напечатаете новую строку (или до тех пор, пока буфер не будет заполнен, если у вас действительно длинные строки), поэтому вы чаще всего видите эту проблему при попытке выполнить однострочные запросы с помощью putStr re.

Я предлагаю определить небольшую вспомогательную функцию, подобную этой, чтобы позаботиться о том, чтобы сделать промывку для вас:

import System.IO

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

Теперь вы можете просто сделать

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

Ответ 2

IO происходит в правильном порядке. Проблема буферизации. Если вы запустили stdout после каждого putStr, он должен работать как ожидающий. Вам нужно импортировать hFlush и stdout из System.IO.

Ответ 3

Проблема заключалась не в порядке операций в коде ввода-вывода. Проблема была введена, и вывод по умолчанию буферизируется при использовании stdin и stdout. Это повышает производительность ввода-вывода в приложении, но может привести к тому, что операции будут отображаться не в порядке, когда используются как stdin, так и stdout.

Существует два решения. Вы можете использовать метод hFlush для принудительной очистки дескриптора (stdin или stdout). Например, hFlush stdout, hFlush stdin. Более простое решение (прекрасно подходящее для интерактивных приложений) - полностью отключить буферизацию. Вы можете сделать это, вызвав методы hSetBuffering stdout NoBuffering и hSetBuffering stdin NoBuffering, прежде чем запускать свою программу (т.е. Поместите эти строки в свой основной метод.