在过去的几周里,我一直在尝试独立学习Haskell。目前,我正在尝试实施一个愚蠢的小猜谜游戏,其中计算机选择一个随机数,用户试图猜测它。如果用户错了,程序会告诉用户答案更高或更低,并允许用户猜测直到他们猜对了。我已经有了它的工作,但我想添加跟踪用户每场比赛的猜测次数的能力,并在他们正确猜测后向用户报告该数字。
来自命令性的背景,自然要做的就是每次用户猜测时都会增加一个计数器,但你不能在Haskell中真正做到这一点(至少看起来像是无国籍状态和一切的不变性都会阻止这种情况。)
我想到让getGuess和giveHints函数采用一个额外的参数来表示到目前为止的猜测次数(让我们称之为numGuesses),并且在每次调用这些方法时,传递(numGuesses + 1)。但我无法让它工作(更不用说我甚至不知道这是否有效)。
我的代码如下。任何建议都会非常感激。我主要是在寻找想法,但也可以随意发布实际代码。另外,如果你发现任何令人发指的事情,我可以随时告诉我,如果我的代码糟透了,以及如何改进它(我只是在功能上进行了几周的编程!)
import System.Random
import System.IO
import Control.Monad
main = do
gen <- getStdGen
let (ans,_) = randomR (1,100) gen :: (Int,StdGen)
putStrLn $ "I'm thinking of a number between 1 and 100..."
getGuess ans
putStrLn "You guessed it in __ guesses!"
putStr "Play again? "
hFlush stdout
desire <- getLine
when ((desire !! 0) `elem` ['y','Y']) $ do
putStrLn ""
newStdGen
main
getGuess ans = do
putStr "Your guess? "
hFlush stdout
guessStr <- getLine
giveHints ans (read guessStr)
giveHints ans guess = do
when (ans /= guess) $ do
if ans > guess
then putStrLn "It's higher."
else putStrLn "It's lower."
getGuess ans
注意:我正在使用hFlush标准输出,因为我正在使用行缓冲,没有它,某些交互的顺序不是人们所期望的。
答案 0 :(得分:8)
你实际上可以实现你正在考虑的计数方法,但你仍然需要明确地传递状态。但在这种情况下,它根本不是一件麻烦事。事实上,这是一个人们经常看到辅助函数的模式,实际上使用State
monad会有点矫枉过正。
我所指的模式通常如下:
doStuff xs' = go xs' 0
where
go (x:xs) n = .. etc ..
这是代码。
import System.Random (randomRIO)
import Control.Applicative ((<$>))
import Control.Monad (when)
import Text.Printf (printf)
playGame :: Int -> Int -> IO ()
playGame answer curGuesses = do
putStrLn "What is your guess?"
putStr ">"
guess <- getGuessFromUser
when (guess /= answer) $ do
giveHints answer guess
playGame answer (curGuesses + 1)
when (guess == answer) $ do
putStrLn "You guessed it!"
printf "You guessed %d times!\n" (curGuesses + 1)
giveHints :: Int -> Int -> IO ()
giveHints answer guess
| answer > guess = putStrLn "It's higher!"
| otherwise = putStrLn "It's lower!"
getGuessFromUser :: IO Int
getGuessFromUser = do
read <$> getLine
main :: IO ()
main = do
answer <- randomRIO (1, 100)
putStrLn "I'm thinking of a number between 1 and 100."
playGame answer 0
备注强>
<$>
是fmap
randomRIO
作为Daniel提到的,因为我们已经在IO monad。hSetBuffering
或hFlush
来获得正确的输出。但是,YMMV。答案 1 :(得分:3)
为猜测数添加额外的参数正是你在功能上如何做这件事。
基本的思维模式是,如果你的某个函数需要根据“某事”的不同值而表现不同,那么这个函数就是函数的参数。这是纯度的简单结果;对于相同的输入,函数必须始终返回相同的内容。
当您使用更高级的技术时,有多种方法可以“隐藏”额外的参数,使您不必显式地写入/传递它们;这基本上就是State
monad的作用,并且考虑IO
monad的一种方式是它正在做类似的事情。但是,当你是函数式编程的新手时,习惯这种思维方式可能更有帮助;您将信息传递给您通过其参数调用的函数,并通过其参数接收信息。您不能诉诸于将信息留在某个外部位置(例如计数器的值)的必要技巧,您知道您调用的函数将查找它(甚至修改它)。