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):
Жирным шрифтом являются то, что я набрал.
Очевидно, у меня есть некоторые основные концептуальные ошибки, но я не знаю, что. Обратите внимание, что он корректно работает в интерпретаторе и сбой при компиляции.
Ответы
Ответ 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
, прежде чем запускать свою программу (т.е. Поместите эти строки в свой основной метод.