首页 文章

Haskell编译的IO动作命令和刷新[重复]

提问于
浏览
4

这个问题在这里已有答案:

我在编译的Haskell代码中遇到了IO的奇怪行为 . 这是发生了什么:

-- MyScript.hs
main = do
    putStr "Enter your name: "
    a <- getLine
    putStrLn (a ++ " - that's a nice name!")

我通过调用 main 在GHCi中运行它,它可以正常工作,首先打印 Enter your name: ,然后做任何事情 . 但是,当我用GHC(有和没有 --make )编译它时,它首先提示输入一行,然后一次打印所有内容,如下所示:

$ ./MyScript
Jimmy Johnson
Enter your name: Jimmy Johnson - That's a nice name!

为了澄清,我希望它按以下顺序发生:

$ ./MyFixedScript
Enter your name: Jimmy Johnson
Jimmy Johnson - That's a nice name!

有人可以解释为什么会发生这种情况,以及如何按照我期望的方式对IO进行排序 .

另请注意,我已尝试将 do 语句的第一行更改为 _ <- putStr "Enter your name: " ,但这仍然无效 .

2 回答

  • 12

    IO操作以正确的顺序发生,问题在于输入和输出管道的工作方式 . 字符串 "Enter your name: "getLine 之前由 putStr 写入输出缓冲区,但缓冲区未必被刷新 . 在 putStr 之后添加 hFlush stdout 将刷新缓冲区 .

    import System.IO
    
    -- MyScript.hs
    main = do
        putStr "Enter your name: "
        hFlush stdout
        a <- getLine
        putStrLn (a ++ " - that's a nice name!")
    
  • 4

    我今天遇到了完全相同的问题,当我使用 putStrLn 时它似乎运行良好但是当我将其更改为 putStr 时停止了 . 正如其他人所说,它与Haskell或GHC无关,而是IO如何被刷新 . 根据 System.IO documentation有3种缓冲模式:

    • line-buffering:只要输出换行符,缓冲区溢出,发出System.IO.hFlush或句柄关闭,就会刷新整个输出缓冲区 .

    • block-buffering:只要溢出,发出System.IO.hFlush或关闭句柄,就会写出整个缓冲区 .

    • no-buffering:输出立即写入,从不存储在缓冲区中 .

    默认缓冲模式被认为是系统相关的,但似乎正常程序处于 line-buffering 模式,而GHCI处于 no-buffering 模式 . 这解释因为使用 putStrLnputStr 将刷新或不刷新 .

    要解决您的问题,您可以使用 hFlush stdout 来刷新explictitey(请参阅Cirdec答案)或通过执行 hSetBuffering stdout NoBuffering 更改缓冲模式一次 . 显然, NoBuffering 模式并不是最佳的,但对于小玩具程序来说可能已经足够了 .

相关问题