为了测试我在 Haskell 中的技能,我决定实现你在Land of Lisp / Realm of Racket中找到的第一款游戏。“猜我的号码”游戏。游戏依赖于可变状态来运行,因为它必须不断更新程序的上限和下限,以了解用户正在考虑的值。
它有点像这样:
> (guess)
50
> (smaller)
25
> (bigger)
37
现在,这种事情(据我所知)在 Haskell 中并不完全可能,从 REPL 中调用一些修改全局可变状态的函数,然后立即打印结果,因为它违反了不变性原则。因此,所有的交互都必须存在于一个IO
和/或State
单子中。这就是我卡住的地方。
我似乎无法将IO
monad 和State
monad 结合起来,所以我可以在同一个函数中获取输入、打印结果和修改状态。
这是我到目前为止得到的:
type Bound = (Int, Int) -- left is the lower bound, right is the upper
initial :: Bound
initial = (1, 100)
guess :: Bound -> Int
guess (l, u) = (l + u) `div` 2
smaller :: State Bound ()
smaller = do
bd@(l, _) <- get
let newUpper = max l $ pred $ guess bd
put $ (l, newUpper)
bigger :: State Bound ()
bigger = do
bd@(_, u) <- get
let newLower = min u $ succ $ guess bd
put $ (newLower, u)
我现在需要做的就是想办法
- 打印初始猜测
- 接收想要更小/更大数字的命令
- 相应地修改状态
- 递归调用函数,让它再次猜测
我如何结合IO
并State
以优雅的方式实现这一目标?
注意:我知道这可能完全不用状态就可以实现;但我想让它保持原汁原味