所以我正在写一个流氓,我需要随机生成一些地牢。我可以使用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中的种子,我听说它非常慢,尽管我不知道为什么。但这对我来说已经足够了。