哈斯克尔 – 猜猜我的号码,一个单一的头痛
为了测试我在 Haskell中的技能,我决定实现你在Land of Lisp / Realm of Racket中找到的第一款游戏. “猜猜我的号码”游戏.游戏依赖于可变状态来运行,因为它不断地更新程序的上限和下限以回归用户所考虑的值. 它有点像这样: > (guess) 50 > (smaller) 25 > (bigger) 37 现在,这种事情(据我所知)在Haskell中完全不可能,从REPL中调用一些修改全局可变状态的函数,然后立即打印结果,因为它违反了不变性原则.因此,所有交互必须存在于IO和/或State monad中.那就是我被困住的地方. 我似乎无法将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来实现这一目标? 注意:我知道这可以在不使用状态的情况下实现;但我想让它忠于原作 您可以使用monad变换器组合不同的monad – 在本例中为StateT.您可以通过更改类型签名来使用现有代码以使用StateT:bigger,smaller :: Monad m => StateT Bound m () 那么你可以编写一个函数来给出一个状态参数来运行游戏: game :: StateT Bound IO () game = do s <- get liftIO $print (guess s) verdict <- (liftIO getLine) case verdict of "smaller" -> smaller >> game "bigger" -> bigger >> game "ok" -> return () _ -> (liftIO $putStrLn $"Unknown verdict " ++ verdict) >> game 您使用liftIO将IO动作提升到StateT Bound IO monad,允许您提示输入并读取下一行. 最后,您可以使用runStateT运行游戏: runStateT game initial (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |