我是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(输入无字符),那么它只会被打印一次。任何人都可以向我解释我做错了什么?
感谢。
答案 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
。