dec*_*ion 4 haskell monad-transformers
我正在编写一些代码(围绕卡片播放策略),State它们一起使用和递归.也许这一部分实际上并不需要(它对我来说已经感到笨拙,即使是相对初学者),但还有其他部分可能这样做我的一般问题代表...
我最初的天真实现完全是确定性的(出价的选择只是函数提供的第一个选项validBids):
bidOnRound :: (DealerRules d) => d -> NumCards -> State ([Player], PlayerBids) ()
bidOnRound dealerRules cardsThisRound = do
(players, bidsSoFar) <- get
unless (List.null players) $ do
let options = validBids dealerRules cardsThisRound bidsSoFar
let newBid = List.head $ Set.toList options
let p : ps = players
put (ps, bidsSoFar ++ [(p, newBid)])
bidOnRound dealerRules cardsThisRound
Run Code Online (Sandbox Code Playgroud)
我称之为:
playGame :: (DealerRules d, ScorerRules s) => d -> s -> StateT Results IO ()
...
let (_, bidResults) = execState (bidOnRound dealerRules cardsThisRound) (NonEmpty.toList players, [])
Run Code Online (Sandbox Code Playgroud)
现在我知道我需要在这个和代码的其他几个部分中加入随机性.不想垃圾IO随处可见,也没有手动传递轮的随机种子的时候,我觉得我应该用MonadRandom 或东西.我正在使用的库使用它可以产生良好的效果.这是明智的选择吗?
这是我试过的:
bidOnRound :: (DealerRules d, RandomGen g) => d -> NumCards -> RandT g (State ([Player], PlayerBids)) ()
bidOnRound dealerRules cardsThisRound = do
(players, bidsSoFar) <- get
unless (List.null players) $ do
let options = Set.toList $ validBids dealerRules cardsThisRound bidsSoFar
rnd <- getRandomR (0 :: Int, len options - 1)
let newBid = options List.!! rnd
let p : ps = players
put (ps, bidsSoFar ++ [(p, newBid)])
bidOnRound dealerRules cardsThisRound
Run Code Online (Sandbox Code Playgroud)
但我已经不舒服,再加上不能工作了如何使用调用此,例如evalRand与组合execState等我越读MonadRandom,RandGen和mtlVS别人,少肯定我是我在做什么?
我应该如何巧妙地结合随机性和State如何正确地称呼这些?
谢谢!
编辑:供参考,Github上的完整电流源.
那么一个例子可以帮助你.由于您没有发布完整的工作代码片段,因此我只需更换您的大量操作并展示如何评估monad:
import Control.Monad.Trans.State
import Control.Monad.Random
import System.Random.TF
bidOnRound :: (RandomGen g) => Int -> RandT g (State ([Int], Int)) ()
bidOnRound i =
do rand <- getRandomR (10,20)
s <- lift $ get
lift $ put ([], i + rand + snd s)
main :: IO ()
main =
do g <- newTFGen
print $ flip execState ([],1000) $ evalRandT (bidOnRound 100) g
Run Code Online (Sandbox Code Playgroud)
这里要注意的是你首先"打开"外部monad.所以,如果你有,RandT (StateT Reader ...) ...那么你运行RandT(ex via evalRandT或类似)然后是状态然后是读者.其次,你必须lift从外部monad中使用内部monad上的操作.这可能看起来很笨拙,因为它非常笨拙.
我所知道的最好的开发人员 - 那些我喜欢看和使用的代码 - 提取monad操作并提供一个完整的所有原语的API,所以当我考虑的时候我不需要考虑monad的结构我写的逻辑结构.
在这种情况下(由于我在没有任何应用领域,押韵或理由的情况下编写上述内容,因此我会写一下):
type MyMonad a = RandT TFGen (State ([Int],Int)) a
runMyMonad :: MyMonad () -> IO Int
runMyMonad f =
do g <- newTFGen
pure $ snd $ flip execState ([],1000) $ evalRandT f g
Run Code Online (Sandbox Code Playgroud)
将Monad定义为简单的别名和执行操作,基本功能更容易:
flipCoin :: MyMonad Int
flipCoin = getRandomR (10,20)
getBaseValue :: MyMonad Int
getBaseValue = snd <$> lift get
setBaseValue :: Int -> MyMonad ()
setBaseValue v = lift $ state $ \s -> ((),(fst s, v))
Run Code Online (Sandbox Code Playgroud)
通过这项工作,这通常是制作实际应用程序的一小部分,特定于域的逻辑更容易编写,当然更容易阅读:
bidOnRound2 :: Int -> MyMonad ()
bidOnRound2 i =
do rand <- flipCoin
old <- getBaseValue
setBaseValue (i + rand + old)
main2 :: IO ()
main2 = print =<< runMyMonad (bidOnRound2 100)
Run Code Online (Sandbox Code Playgroud)