Haskell помещает putStr и putStrLn в конец программы, а не во время выполнения - PullRequest
2 голосов
/ 11 июля 2019

У меня есть простая программа, которая просто берет строку у пользователя и ключ и шифрует строку с помощью функции Цезаря. Сама функция работает, поэтому я не буду показывать источник для этого. Проблема в том, что когда компилятор компилирует программу, он позволяет мне вводить все getLines, а затем, после ввода всего, программа выводит все putStr и putStrLn, а затем закрывается. Это ТОЛЬКО происходит, когда программа выполняется с использованием "runhaskell" или компилируется и выполняется как исполняемый файл, т.е. не в переводчике. Вот программа:

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» не является проблемой, потому что я заменил все варианты использования prompt на их соответствующие putStr и getLine, и он все еще делал то же самое.

1 Ответ

7 голосов
/ 11 июля 2019

runhaskell и ghci предназначены для запуска вашей программы как можно быстрее и снижения эффективности запуска программы. По этой причине они принимают множество неоптимальных решений по эффективности по сравнению с ghc, и одно из них, которое вас здесь кусает, заключается в том, что они не используют буферизацию на стандартном вводе или выводе по умолчанию, тогда как ghc использует более эффективную буферизацию строки по умолчанию. , Поскольку вы никогда не печатаете строку, заканчивающуюся в течение ваших prompt s, в скомпилированной версии буфер не показывается пользователю ... до тех пор, пока вы не достигнете putStrLn в конце программы, которая печатает конец строки, и весь буфер показывается сразу.

У вас есть несколько вариантов:

  1. Явно запросить, чтобы не было никакой буферизации. Это сделает вашу скомпилированную программу несколько медленнее (очень маловероятно, что ее заметят при скорости взаимодействия с человеком), но будет вести себя больше как интерпретируемая версия. Импортируйте System.IO и используйте hSetBuffering stdout NoBuffering в начале main.
  2. Явно очищайте буфер, когда вы знаете, что собираетесь сделать паузу для пользовательского ввода. Импортируйте System.IO и используйте hFlush stdout перед каждым вызовом getLine.
  3. Выводить способом, который ведет себя одинаково между двумя режимами буферизации. Используйте putStrLn вместо putStr везде.
...