2014-01-09 93 views
1

我是Haskell的新手,一直試圖更好地理解IO monad(在使用純函數玩了一段時間之後)。Haskell中的自定義循環打印兩次語句

我下面就以IO monad

之一演習正在一會兒功能的教程。他們沒有顯示一個例子,所以你不能檢查你的答案。

這裏是我的:

while :: IO Bool -> IO() 
while action = do p <- action 
        if p then putStrLn "You win!" >> return() 
         else putStrLn "Nope. Try again!" >> while action 


main = do putStrLn "Come and guess the letter!" 
      while (constAskForC) 
      where constAskForC = do c <- getChar 
            return $ c == 'c' 

現在,我的問題是,如果你輸入一個錯誤的字符(幾乎是一個字,是不是「C」),然後串「不再試一次!」打印兩次到StdOut。爲什麼是這樣?這裏的程序運行:

Come and guess the letter! 


"Nope. Try again!" 

"Nope. Try again!" 
d 
"Nope. Try again!" 
"Nope. Try again!" 

"Nope. Try again!" 

"Nope. Try again!" 
a 
"Nope. Try again!" 
"Nope. Try again!" 
d 
"Nope. Try again!" 
"Nope. Try again!" 
f 
"Nope. Try again!" 
"Nope. Try again!" 
a 
"Nope. Try again!" 
"Nope. Try again!" 
s 
"Nope. Try again!" 
"Nope. Try again!" 

如果你只是按回車(輸入沒有字符),那麼它只會打印一次。任何人都可以向我解釋我做錯了什麼?

謝謝。

+2

我最初的猜測是緩衝模式。導入'System.IO'後試試'hSetBuffering stdout LineBuffering',看看是否解決了你的問題。 – bheklilr

+2

你輸入一個字母並按下回車鍵。一封信是一個字符。Enter是另一個字符。這給出了兩個字符,數它們;) –

回答

4

問題出在constAskForC函數中。您使用getChar,但那隻會讀取一個字符。所以你讀了c,當你讀c後,你會得到行尾字符(\n)。實在是沒有辦法讓單個字符,但你可以得到一整行,只需要第一個字符:

main = do putStrLn "Come and guess the letter!" 
      while (constAskForC) 
    where constAskForC = do c <- getLine 
          return $ case c of 
          [] ->  False -- There was no input    
          'c':[] -> True -- The first letter was a c, and nothing else was entered 
          _  -> False -- Otherwise, the result is False 

您的代碼另一個小記:putStrLn "You win!" >> return()相同putStrLn "You win!"

+0

啊,忘了是一個字符。謝謝! – Nacht

2

當你輸入一個字符,然後按回車,實際上是兩個字符(可打印的字符加一個換行字符)。您可能想要使用getLine

4

這裏的問題是getChar命令的行爲與正常的 緩衝行爲之間的交互作用,該編譯程序使用行緩衝。

getChar命令只消耗一個字符。特別是,點擊返回會自行創建一個換行符。

但是,使用行緩衝,在輸入完整行之前實際上沒有輸入。 所以如果你插入一個字符和回車鍵,將會一次生成兩個字符,導致奇怪的輸出。

可以通過添加行

import System.IO 

在一開始,然後添加語句

hSetBuffering stdin NoBuffering 

main程序解決此問題。或者,使用默認使用NoBuffering的GHCi。