2014-12-05 2 views
3

Я встречаю странное поведение с IO в рамках скомпилированного кода Haskell. Вот что происходит:Haskell Скомпилированный порядок ввода-вывода и промывка

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

я запускаю это в GHCi по телефону main и он работает, как и следовало ожидать, первая печать 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! 

Может кто-нибудь объяснить, почему это происходит, как это, и как последовательность ввода-вывода так, что я бы ожидать его.

Обратите внимание, что я попытался изменить первую строку инструкции do на _ <- putStr "Enter your name: ", но это все еще не работает.

+1

Это связано с буферизацией, не упорядочивающей действия io. – sabauma

ответ

12

Действия IO происходят в правильном порядке, проблема заключается в том, как работают входные и выходные трубы. Строка "Enter your name: " записывается в выходной буфер putStr до getLine, но буфер не обязательно был сброшен. Добавление hFlush stdout после putStr приведет к сбросу буфера.

import System.IO 

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

Ух ты, я подумал, что это могло быть так, но я не верил, что Хаскелл на самом деле будет подчиняться состоянию состояния, например, стиранию stdout ... – AJFarmar

+1

@AJFarmar: В этом случае Haskell просто дает вам довольно прямой интерфейс к той же базовой абстракции канала, что и другие языки программирования.Так как он спрятан внутри 'IO', он может просто точно воспроизвести поведение. –

+0

И я думаю, что GHCi сбрасывается автоматически? – AJFarmar

4

У меня точно такая же проблема сегодня, и кажется, что он работал хорошо, когда я использовал putStrLn, но остановился, когда я изменил его на putStr. Как говорили другие люди, это не связано с Haskell или GHC, но как IO размыты. Согласно System.IOdocumentation есть 3 режима буферизации:

  • буферизация строк: весь выходной буфер сбрасывается всякий раз, когда выводится символ новой строки, то переполнение буфера, A System.IO.hFlush выдается или ручка закрыто.
  • блок-буферизация: весь буфер выписывается всякий раз, когда он переполняется, выдается System.IO.hFlush или дескриптор закрывается.
  • no-buffering: вывод записывается немедленно и никогда не сохраняется в буфере.

режим буферизации по умолчанию называется besystem зависит, но мне кажется, что нормальная программа находится в режиме line-buffering, в то время как GHCI находится в режиме no-buffering. Это объясняется тем, что использование putStrLn или putStr будет заподлицо или нет.

Чтобы решить вашу проблему, вы можете использовать hFlush stdout, чтобы очистить explictitey (см. Ответ Cirdec) или изменить режим буферизации один раз, выполнив hSetBuffering stdout NoBuffering. Obvioulsly режим NoBuffering не оптимален, но, вероятно, достаточно для маленькой игрушечной программы.

+0

Ручка для 'hSetBuffering' является первым аргументом. 'hSetBuffering :: Handle -> BufferMode -> IO()' поэтому установка 'NoBuffering' на' stdout' будет 'hSetBuffering stdout NoBuffering'. – Cirdec

+0

@Cirdec Я исправил его – mb14

Смежные вопросы