2011年4月13日 星期三

Haskell 與 State Monad

昨天試著用 HaskellState Monad,花了一點時間才寫出一個簡單的猜數字遊戲。感覺要在 Functional Programming Language 寫出 State 的概念比較困難...

import Control.Monad.State
import System.IO
import System.Random

gameLB, gameUB :: Integer
gameLB = 0
gameUB = 100

data GameState = GameState { steps :: Integer ,
                             lb :: Integer,
                             ub :: Integer }

initialState :: Integer -> Integer -> GameState
initialState l u = GameState { steps = 0, lb = l, ub = u }

updateState :: Integer -> Integer -> GameState -> GameState
updateState ans guess (GameState { steps = s, lb = l, ub = u }) =
    case (compare ans guess) of
      EQ -> GameState { steps = s + 1, lb = ans, ub = ans }
      LT -> GameState { steps = s + 1, lb = l, ub = min (guess - 1) u }
      GT -> GameState { steps = s + 1, lb = max l (guess + 1), ub = u }

range :: GameState -> (Integer, Integer)
range (GameState { lb = l, ub = u }) = (l , u)

stepCount :: GameState -> Integer
stepCount (GameState { steps = s }) = s

gameSession :: Integer -> StateT GameState IO Integer
gameSession ans =
    do (l, u) <- gets range
       lift $ putStr $ "Guess number (" ++ show l ++ ".." ++ show u ++ "): "
       lift $ hFlush stdout
       str <- lift $ getLine
       let num = read str
       modify (updateState ans num)
       case (compare ans num) of
         EQ -> gets stepCount >>= return
         LT -> gameSession ans >>= return
         GT -> gameSession ans >>= return

main :: IO ()
main = do rand <- getStdRandom (randomR (gameLB, gameUB))
          s <- evalStateT (gameSession rand) (initialState gameLB gameUB)
          putStrLn $ "Steps used: " ++ show s

備註:上面的程式用 ghc --make Guess.hs 就可以編譯了(有用到 mtl package

沒有留言:

張貼留言