【问题标题】:Haskell placing putStr and putStrLn at the end of a program instead of during executionHaskell 将 putStr 和 putStrLn 放在程序的末尾而不是在执行期间
【发布时间】:2019-07-11 16:51:27
【问题描述】:

我有一个简单的程序,它只从用户那里获取一个字符串和一个密钥,并使用凯撒密码函数对该字符串进行加密。该功能本身有效,因此我不会为此提供来源。问题是,当编译器编译程序时,它将允许我输入所有 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 有什么我忽略的吗?它们是否仅作为函数或其他东西的结果执行?

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

【问题讨论】:

  • 试试prompt str = do { putStr str; hFlush stdout; getLine }
  • stdout 默认情况下是行缓冲的。除非你填满整条线,否则你需要冲洗它。使用do putStr "..." ; hFlush stdout ; getLine
  • 或者使用hSetBuffering stdout NoBuffering(来自System.IO)。

标签: haskell ghc ghci


【解决方案1】:

runhaskellghci 旨在尽快启动您的程序,并淡化运行程序的效率。出于这个原因,与ghc 相比,它们做出了许多次优的效率决策,其中一个让你感到困扰的是,它们默认不使用标准输入或输出缓冲,而ghc 默认使用更有效的行缓冲.由于您在 prompts 期间从不打印行尾,因此在编译版本中,缓冲区不会显示给用户......直到您到达打印行尾的程序末尾的 putStrLn,并且立即显示整个缓冲区。

你有一些选择:

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

【讨论】:

  • 这可以说是标准库中的一个错误。如果两者都是行缓冲的(或者当两者都链接到终端时),它应该在自动从标准输入读取之前刷新标准输出。
猜你喜欢
  • 2014-03-18
  • 2022-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多