考虑以下(非常沉闷)游戏: - 玩家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
答案 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个,没有暴力。