首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >MonadRandom、状态变压器和单台变压器

MonadRandom、状态变压器和单台变压器
EN

Stack Overflow用户
提问于 2017-10-18 05:51:21
回答 1查看 328关注 0票数 4

我正在编写一些代码(围绕着打牌策略),它将State和递归结合在一起。也许这部分不需要实际的(我已经觉得笨拙,即使是相对初学者),但还有其他部分可能这样做,我的一般问题是.

我最初的天真实现完全是确定性的(对出价的选择只是函数validBids提供的第一个选项):

代码语言:javascript
复制
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

我称它为:

代码语言:javascript
复制
playGame :: (DealerRules d, ScorerRules s) => d -> s -> StateT Results IO ()
  ...
let (_, bidResults) = execState (bidOnRound dealerRules cardsThisRound) (NonEmpty.toList players, [])

现在,我意识到我需要将随机性引入到这个部分和代码的其他部分中。我不想到处乱扔IO,也不想一直手工传递随机种子,我觉得我应该使用MonadRandom之类的东西。我用的一个图书馆效果很好。这是明智的选择吗?

以下是我尝试过的:

代码语言:javascript
复制
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

但我已经很不舒服了,而且我也想不出怎么称呼它,比如使用evalRandexecState等。我读到的关于MonadRandomRandGenmtl对其他人的文章越多,我就越不确定自己在做什么……

我应该如何将随机性和State巧妙地结合起来,以及如何正确地将它们命名为?

谢谢!

编辑:供参考,吉突布上的全电流源

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-10-18 06:32:14

不如举个例子来帮你。由于您没有发布完整的工作代码片段,所以我将替换大量操作,并演示如何评估monads:

代码语言:javascript
复制
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

这里要注意的是,您首先要“解开”外部单片。因此,如果您有RandT (StateT Reader ...) ...,那么您将运行RandT (例如通过evalRandT或类似的方式),然后运行状态,然后运行读取器。其次,您必须从外部monad中lift来使用内部monad上的操作。这可能看起来很笨拙,这是因为它是可怕的笨拙。

我所认识的最好的开发人员--那些我喜欢看和使用的代码--提取monad操作,并提供一个包含所有原语的API,所以我在考虑我正在编写的逻辑结构时,不需要考虑monad的结构。

在本例中(由于我没有任何应用程序域、押韵或原因而编写了上面的内容),您可以编写:

代码语言:javascript
复制
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定义为简单的别名和执行操作,基本功能更容易实现:

代码语言:javascript
复制
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))

由于这一段工作(这通常是实际应用程序的一小部分),域特定的逻辑更容易编写,当然也更容易阅读:

代码语言:javascript
复制
bidOnRound2 :: Int -> MyMonad ()
bidOnRound2 i =
 do rand <- flipCoin
    old  <- getBaseValue
    setBaseValue (i + rand + old)

main2 :: IO ()
main2 = print =<< runMyMonad (bidOnRound2 100)
票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46803597

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档