可以在不使用可变变量的情况下对其进行编码吗?

时间:2018-01-24 12:03:35

标签: haskell

考虑以下(非常沉闷)游戏:      - 玩家A认为1到100之间的数字。      - 玩家B可以尝试5次猜测该号码。玩家A将对每个猜测做出“太大”,“太小”或“正确”的反应。

我想在Haskell中模拟这个,当然这很简单。但是为了让事情变得有趣,我想以玩家B不能“欺骗”的方式编写代码。这意味着两件事:      - 不允许玩家B代码查看密码的正确值。播放器A代码为播放器B代码提供了一个函数,用于检查其猜测。      - 不允许玩家B代码调用该函数超过五次。不知何故,播放器A代码必须计算函数被调用的次数。

使用私有可变变量在OO语言中很容易实现。

在Haskell中,我使用IORef对其进行编码以保持呼叫次数的计数。那很好,我认为我的解决方案是正确的。但我的问题是:

“这可以在没有IORef或类似的情况下在Haskell中完成吗?是否有一个我错过的纯功能解决方案?”

这是我的Haskell代码:

import Data.IORef (newIORef, readIORef, writeIORef)
import System.Random (randomRIO)

lowest = 1
highest = 100
maxtries = 5

playerA :: IO (Int -> IO (Maybe Ordering))
playerA = do
    secret <- randomRIO (lowest, highest)
    print ("Secret", secret)
    tries <- newIORef maxtries
    return $ \ guess -> do
        t <- readIORef tries
        if t == 0 then
            return Nothing
        else do
            writeIORef tries $ t - 1
            return $ Just $ guess `compare` secret

playerB :: (Int -> IO (Maybe Ordering)) -> Int -> Int -> IO ()
playerB guessfunc lowbound highbound = do
    let guess = (lowbound + highbound) `div` 2
    response <- guessfunc guess
    print (lowbound, highbound, guess, response)
    case response of
        Just GT -> playerB guessfunc lowbound (guess - 1)
        Just LT -> playerB guessfunc (guess + 1) highbound
        Just EQ -> putStrLn "Player B wins"
        Nothing -> putStrLn "Player B loses"

main :: IO ()
main = do
    guessfunc <- playerA
    playerB guessfunc lowest highest

2 个答案:

答案 0 :(得分:11)

没有必要在IO中执行此操作,您也可以使用纯状态monad:

import Control.Monad.Trans.State
import System.Random (randomRIO)

maxtries = 5

playerA :: IO (Int -> State Int (Maybe Ordering))
playerA = do
   secret <- randomRIO (1,100)
   return $ \guess -> state $ \tries
          -> if tries < maxtries then (Just $ guess`compare`secret, tries+1)
                                 else (Nothing, tries)

playerB :: Monad m => (Int -> m (Maybe Ordering)) -> Int -> Int -> m (Maybe Int)
playerB guessfunc lowbound highbound = do
    let guess = (lowbound + highbound)`div`2
    response <- guessfunc guess
    case response of
        Just GT -> playerB guessfunc lowbound (guess - 1)
        Just LT -> playerB guessfunc (guess + 1) highbound
        Just EQ -> return $ Just guess
        Nothing -> return Nothing

main = do
   pa <- playerA
   print . (`evalState`0) $ playerB pa 1 100

请注意,playerB不知道monad是State Int。这很重要,因此她不能通过在调用guessfunc之间操纵状态变量来作弊。

答案 1 :(得分:0)

请注意,您正在使用IO进行随机生成并跟踪剩余的尝试。数字本身可以隐藏在最纯粹的monadless函数中:

playerA :: Int -> Int -> Ordering
playerA = compare

这样,playerA 42是一个不透明的(就结构而非纯度而言)对象,当用数字输入时返回Ordering,但你永远无法通过报告中描述的任何方式检索它中的42个,没有暴力。