Как сделать puremt в монаде, используя пакет random-fu - PullRequest
0 голосов
/ 28 ноября 2010

Итак, я пишу roguelike и мне нужно генерировать несколько темниц случайным образом. Я мог бы использовать system.random, но я действительно хочу сделать его чистым. Я хочу использовать newPureMT, чтобы сгенерировать случайный источник мерсеннового твистера, а затем передать его в некоторые монадные преобразователи, такие как stateT и readerT и некоторые другие. Так что я бы закончил с чем-то вроде:

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

Я не могу понять, как это сделать, не оставаясь в монаде IO. Например, он дает этот пример:

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

Это в значительной степени позволяет мне делать то, что я хотел синтаксически, и использует мерсенновый твистер, так что это должно быть чертовски быстрее. Единственное, что меня беспокоит, это то, что при каждом поколении чисел оно обновляет начальное число в монаде состояния, и я слышал, что оно действительно медленное, хотя я не знаю почему. Но этого должно быть достаточно для моих целей.

Ответы [ 2 ]

1 голос
/ 28 ноября 2010

Я не скажу, что это лучший ответ, чем Пол Джонсон, но это один из способов сделать это, если я понял, что вы хотели:

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
    ...
    ...

Я использую стандартный модуль генератора случайных чисел, так как я 'Мы никогда не использовали random-fu, но принцип должен быть таким же, просто совершите случайное действие, если его нет в библиотеке.

Я использую классы типов, чтобы сохранить общий код, вы можете использоватьэто с любой монадой (преобразователем), которая является экземпляром класса-типа MonadState.Вам не нужно использовать классы типов, вместо этого вы можете использовать свою конкретную монаду в типе rand.

1 голос
/ 28 ноября 2010

Для чего-то подобного я бы порекомендовал использовать монаду Gen от QuickCheck.Он предназначен для создания случайных структур данных, поэтому вам нужно определить свое подземелье как структуру данных, а затем написать для него «произвольный» экземпляр (см. Документацию по QuickCheck).

Ваше мышление похоже на линиюиз "получить несколько случайных чисел, а затем превратить их в темницу".Лучший способ мышления - «генерировать случайные элементы подземелья и случайным образом соединять их».

...