Haskell中的自定义while循环打印两次语句

时间:2014-01-09 16:33:32

标签: haskell io stdout monads

我是Haskell的新手并且一直在努力更好地理解IO monad(在使用纯函数一段时间之后)。

我正在关注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'

现在,我的问题是,如果你输入了一个错误的角色(几乎不是一个不是&#39; c&#39;),那么字符串&#34; Nope。再试一次!&#34;被打印两次到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!"

如果您只是按Enter(输入无字符),那么它只会被打印一次。任何人都可以向我解释我做错了什么?

感谢。

3 个答案:

答案 0 :(得分: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!"

相同

答案 1 :(得分:4)

这里的问题是getChar命令的行为与法线的交互 编译程序的缓冲行为,即使用行缓冲。

getChar命令仅消耗一个字符。特别是,点击返回会自行创建一个换行符。

但是,使用行缓冲,在输入完整行之前,实际上不会输入任何输入。 因此,如果您对一个字符和Return键进行交错,则会一次生成两个字符,从而导致奇怪的输出。

您可以通过添加行

来解决此问题
import System.IO

在开头然后添加语句

hSetBuffering stdin NoBuffering

main计划。或者,使用默认情况下使用NoBuffering的GHCi。

答案 2 :(得分:2)

键入字符并按Enter键时,实际上是两个字符(可打印字符加上换行符)。您可能希望改为使用getLine