haskell - 如何在一个简单的猜谜游戏中跟踪猜测的次数(Haskell)

标签 haskell random state

在过去的几周里,我一直在尝试独立学习 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 标准输出,因为我使用的是行缓冲,没有它,一些交互的顺序就不是人们所期望的。

最佳答案

您实际上可以实现您正在考虑的计数方法,但您仍然必须明确地传递状态。但在这种情况下,这根本不是一件麻烦事。事实上,这是一种在帮助函数中经常看到的模式,实际使用 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正如丹尼尔所说,因为我们已经在 IO 单子(monad)中。
  • 我不必使用 hSetBufferinghFlush在 Windows 上使用命令提示符获得正确的输出。然而,YMMV。
  • 关于haskell - 如何在一个简单的猜谜游戏中跟踪猜测的次数(Haskell),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11856135/

    相关文章:

    arrays - 更新状态数组中的一个值 react 原生

    haskell - 为什么会出现这种类型错误?

    字符串上的 Haskell 模式匹配 - 为什么这不起作用?

    c++ - 随机 std::array

    reactjs - 单击另一个组件时渲染一个组件

    javascript - React setTimeout Hook onclick

    windows - Windows 上 Haskell 中的 Unicode 控制台 I/O

    haskell - 在 Haskell 中使用 'Either'

    python - 线性同余生成器——如何选择种子和统计检验

    python - 将 random.choice 与 if/elif/else 语句一起使用