gpt4 book ai didi

haskell - MonadRandom、State 和 monad 转换器

转载 作者:行者123 更新时间:2023-12-04 23:38:25 24 4
gpt4 key购买 nike

我正在编写一些使用 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

我称之为:
playGame :: (DealerRules d, ScorerRules s) => d -> s -> StateT Results IO ()
...
let (_, bidResults) = execState (bidOnRound dealerRules cardsThisRound) (NonEmpty.toList players, [])

现在我知道我需要将随机性带入代码的这个部分和其他几个部分。不想乱扔垃圾 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

但我已经不舒服了,而且不知道如何称呼它,例如使用 evalRand结合 execState等等。我在 MonadRandom 上读的越多, RandGenmtl与其他人相比,我对自己在做什么不太确定......

我应该如何巧妙地结合随机性和 State以及如何正确调用这些?

谢谢!

编辑:供引用, full current source on 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

这里要注意的是,您首先“解开”外部 monad。所以如果你有 RandT (StateT Reader ...) ...然后你运行 RandT(例如通过 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

将 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))

有了这些工作,这通常是制作真正应用程序的一小部分,领域特定的逻辑更容易编写,当然也更容易阅读:
bidOnRound2 :: Int -> MyMonad ()
bidOnRound2 i =
do rand <- flipCoin
old <- getBaseValue
setBaseValue (i + rand + old)

main2 :: IO ()
main2 = print =<< runMyMonad (bidOnRound2 100)

关于haskell - MonadRandom、State 和 monad 转换器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46803597/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com