我是一名Haskell初学者,我刚刚开始围绕Monads,但我还没有真正得到它。我正在写一个游戏,包括询问用户输入和响应。这是我的函数的简化版本:
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
它是否正确? (是/否):
我输入的内容是粗体。
显然,我有一些重大的概念错误,但我不知道是什么。请注意,它在解释器中正常工作,并在编译时失败。
答案 0 :(得分:48)
正如迈克尔所说,问题在于缓冲。默认情况下,输出会被缓冲,直到您打印换行符(或者如果您有非常长的行,则缓冲区已满),因此在尝试使用putStr
这样的同一行提示时,您最常会看到此问题你正在做。
我建议像这样定义一个小辅助函数来为你做冲洗:
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)
答案 1 :(得分:13)
IO正在以正确的顺序发生。问题是缓冲。如果你在每个putStr之后刷新stdout,它应该按预期工作。您需要从hFlush
导入stdout
和System.IO
。
答案 2 :(得分:11)
问题不在于IO代码中的操作顺序。问题是输入和输出在使用stdin和stdout时默认是缓冲的。这会增加应用程序中IO的性能,但是当使用stdin和stdout时,可能会导致操作无序发生。
这有两个解决方案。您可以使用hFlush
方法强制刷新句柄(stdin或stdout)。例如hFlush stdout
,hFlush stdin
。一个更简单的解决方案(适用于交互式应用程序)是完全禁用缓冲。您可以在启动程序之前调用方法hSetBuffering stdout NoBuffering
和hSetBuffering stdin NoBuffering
来执行此操作(即将这些行放在主方法中。