2014-01-09 64 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。