代码之家  ›  专栏  ›  技术社区  ›  David McHealy

如何用随机fu包在monad中实现puremt

  •  0
  • David McHealy  · 技术社区  · 14 年前

    所以我正在写一个流氓,我需要随机生成一些地牢。我可以使用system.random,但我真的想让它变得纯粹。我想使用newPureMT生成一个mersenne twister随机源,然后将其传递到一些monad变形金刚中,比如stateT和readerT等。所以我最终得到的结论是:

    genDungeon = do
      x <- getRandomNumbers
      genrooms x
      y <- getRandomNumbers
      gendoors x
      etc
    

    如果不呆在伊奥·蒙纳德,我想不出怎么做。例如,它给出了以下示例:

    sample (uniform 1 100) :: State PureMT Int
    

    这意味着我应该能够:

    blah = do
      x <- newPureMT
      runState genDungeon x
    

    但即使把它输入到ghci中也会产生错误

    Overlapping instances for Data.Random.Lift.Lift
                                Data.Functor.Identity.Identity
                                (StateT PureMT Data.Functor.Identity.Identity)
      arising from a use of `sample' at <interactive>:1:0-28
    Matching instances:
      instance [incoherent] (Monad m, MonadTrans t) =>
                            Data.Random.Lift.Lift m (t m)
        -- Defined in Data.Random.Lift
      instance [incoherent] (Monad m) =>
                            Data.Random.Lift.Lift Data.Functor.Identity.Identity m
        -- Defined in Data.Random.Lift
    

    我完全不知道这意味着什么,也不知道该如何解决。

    我花了几天的时间想弄明白这一点,但我完全不知道。很明显,有一些相当重的类型签名和一些东西的提升,我必须申请让东西工作,但我不知道它们是什么或如何做。

    编辑:好的,我在查看Mersenne Twister代码时发现PureMT是randomgen的一个实例,这意味着我可以将它传递到system.random并从中获取纯代码,从而使random fu变得不必要。我还是有点希望我能弄清楚如何让这段代码工作,因为random fu给了你很多额外的能力,比如不同的随机分布,但他会为我的项目做的。

    编辑:好的,所以我最后得到了这个代码:

    rand :: (RandomGen g, MonadState g m) => Int -> Int -> m Int
    rand lo hi = do
        r <- get
        let (val, r') = randomR (lo, hi) r
        put r'
        return val
    
    
    gendungeons = replicateM 10 $ do
      x <- rand 0 24
      y <- rand 4 10
      z <- replicateM 10 $ rand 5 50
      let dungeon = makeadungeonpurelywiththeserandomvalues x y z
      return dungeon
    
    
    test = do
      x <- newPureMT
      let dungeons = runState gendungeons x
      return dungeons
    

    这几乎可以让我做我想做的语法,它使用梅森捻线,所以它应该快得多。唯一让我困扰的是,在每一个数字生成中,它都会更新状态monad中的种子,我听说它非常慢,尽管我不知道为什么。但这对我来说已经足够了。

    2 回复  |  直到 14 年前
        1
  •  1
  •   snk_kid    14 年前

    我不会说这是一个比保罗·约翰逊更好的答案,但这是一种方法,如果我明白你想要什么:

    import Control.Monad.State
    import System.Random
    
    -- utility function to hide plumbing of random generator types.
    rand :: (Random a, RandomGen g, MonadState g m) => a -> a -> m a
    rand lo hi = do
        r <- get
        let (val, r') = randomR (lo, hi) r
        put r'
        return val
    
    (flip runState) (mkStdGen seed) $ do
        x <- rand 0 24
        y <- rand 4 10
        z <- rand 5 50
        ...
        ...
    

    我使用标准的随机生成器模块,因为我从来没有使用过随机的FU,但是原理应该是一样的,只要在数据库中不存在RAND动作就可以。

    我使用类型类来保持代码的泛型,您可以将它与任何monad(transformer)一起使用,它是MonadState类型类的实例。不必使用类型类,如果需要,可以在rand类型中使用特定的monad。

        2
  •  1
  •   Paul Johnson    14 年前

    对于这种情况,我建议使用QuickCheck的Gen monad。它是为创建随机数据结构而设计的,因此您需要将您的地牢定义为一个数据结构,然后为它编写一个“任意”实例(请参阅QuickCheck文档)。

    你的想法似乎是沿着“获取一些随机数,然后将它们转化为地牢”的思路。一种更好的思考方式是“生成一些随机的地牢元素,并将它们随机连接在一起”。

    推荐文章