使用getLine和putStr时,IO发生了无序执行。

39

我是一个Haskell初学者,刚开始理解Monad,但我还没有完全掌握。我正在编写一个游戏,它需要向用户请求输入并做出响应。这是我函数的简化版本:

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):

粗体字是我输入的内容。

显然,我有一些重大概念错误,但我不知道是什么。请注意,它在解释器中正常工作,在编译时失败。

3个回答

65

正如Michael所说,问题在于缓冲。默认情况下,输出会被缓冲,直到您打印一个换行符(或者如果您有非常长的行,直到缓冲区满为止),因此当您尝试使用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)

17
这与C标准I/O库的行为相同,只是stdio有一个技巧,我认为Haskell也应该采用这个技巧,即如果你从stdin读取,则会刷新stdout,如果两者都是tty。 - augustss
2
您还可以使用应用形式:value <- putStr "输入一个值:" *> hFlush stdout *> getLine - CtC

20

IO操作是按正确的顺序进行的。问题在于缓冲。如果在每个putStr之后刷新stdout,它应该可以按预期工作。 您需要从System.IO导入和。


13

问题不在IO代码的操作顺序上。问题在于使用stdin和stdout时输入和输出默认为缓冲区方式,这会提高应用程序中IO的性能,但在同时使用stdin和stdout时可能导致操作看起来乱序。

有两种解决方案。您可以使用hFlush方法来强制清空句柄(无论是stdin还是stdout)。例如hFlush stdouthFlush stdin。一个更简单的解决方案(对于交互式应用程序来说可行)是完全禁用缓冲。您可以在开始程序之前调用hSetBuffering stdout NoBufferinghSetBuffering stdin NoBuffering方法来实现此目的(即将这些行放入您的主方法中)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接