Haskell 将 putStr 和 putStrLn 放在程序的末尾而不是在执行期间

Haskell placing putStr and putStrLn at the end of a program instead of during execution

我有一个简单的程序,它只从用户那里获取一个字符串和一个密钥,然后使用凯撒密码函数对该字符串进行加密。该功能本身有效,因此我不会展示它的来源。问题是,当编译器编译程序时,它将允许我输入所有 getLines,然后在输入所有内容后,程序将打印所有 putStr 和 putStrLn,然后关闭。这仅在程序使用 "runhaskell" 执行或作为 exe 编译和执行时发生,即。不在解释器中。这是程序:

main = do

    choice <- prompt "Would you like to encrypt (1) or decrypt (2)? "

    if choice == "1" then do
        encrypt <- prompt "Enter code for encryption: "
        k       <- prompt "Enter key for the code:    "
        let key = read k in
            putStrLn     ("Encrypted message:         " ++ (caesar key encrypt)    ++ "\n")

    else do
        decrypt <- prompt "Enter code for decryption: "
        k       <- prompt "Enter key for the code:    "
        let key = read k in
            putStrLn     ("Decrypted message:         " ++ (caesar (-key) decrypt) ++ "\n")

    getLine

prompt str = do
    putStr str
    getLine

解释器中运行时的输出:

Prelude Main> main
Would you like to encrypt (1) or decrypt (2)? 1 <- the one is user input
Enter code for encryption: Hello world <- user input
Enter key for the code:    2           <- user input
Encrypted message:         Jgnnq"yqtnf <- program output

编译后执行输出:

1           <- user has to input before the console is printed out
Hello world <--┘
2           <--┘
Would you like to encrypt (1) or decrypt (2)? Enter code for encryption: Enter key for the code:    Encrypted message:         Jgnnq"yqtnf

关于 putStrLn 和 putStr 有什么我忽略的地方吗?它们是否仅作为函数或其他东西的结果执行?

另外,我创建的 "prompt" 函数不是问题,因为我用它们各自的 putStr 和 getLine 替换了提示的所有使用,它仍然做了同样的事情。

runhaskellghci旨在尽可能快地启动您的程序,de-emphasize运行程序的效率。出于这个原因,与 ghc 相比,他们做出了许多 sub-optimal 效率决策,而这里让你感到困扰的是,他们默认在标准输入或输出上不使用缓冲,而 ghc 使用默认情况下更有效的行缓冲。由于您从不打印在 prompt 期间结束的行,因此在编译版本中,缓冲区不会显示给用户......直到您到达打印 a 的程序末尾的 putStrLn行结束,整个缓冲区立即显示。

您有一些选择:

  1. 明确要求没有缓冲。这将使您编译的程序稍微慢一些(在人类交互速度下不太可能被注意到)但表现得更像解释版本。在main.
  2. 开头导入System.IO并使用hSetBuffering stdout NoBuffering
  3. 当您知道要暂停以等待用户输入时,明确刷新缓冲区。在每次调用 getLine.
  4. 之前导入 System.IO 并使用 hFlush stdout
  5. 以在两种缓冲模式之间表现相同的方式进行输出。在任何地方都使用 putStrLn 而不是 putStr