2012-06-15 2 views
6

Может ли кто-нибудь описать, как работают конструктор и функции следующего типа?Haskell Случайное поколение

type Rand a = State StdGen a 

getRandom :: (Random a) => Rand a 
getRandom = get >>= (\r -> let (a,g) = random r in (put g) >> (return a)) 

runRand :: Int -> Rand a -> a 
runRand n r = evalState r $ mkStdGen n 

runRandIO :: Rand a -> IO a 
runRandIO r = randomIO >>= (\rnd -> return $ runRand rnd r) 

getRandoms :: (Random a) => Int -> Rand [a] 
getRandoms n = mapM (\_ -> getRandom) [1..n] 

ответ

8

Давайте начнем с самого начала:

type Rand a = State StdGen a 

Эта строка говорит о том, что Rand a тип синоним типа State, состояние которого определяется StdGen и чье окончательное значение типа a. Это будет использоваться для хранения состояния генератора случайных чисел между каждым запросом для случайного числа.

Код для getRandom может быть преобразован в делать обозначение:

getRandom :: (Random a) => Rand a 
getRandom = do 
    r <- get     -- get the current state of the generator 
    let (a,g) = random r in do -- call the function random :: StdGen -> (a, StdGen) 
    put g     -- store the new state of the generator 
    return a     -- return the random number that was generated 

runRand функция принимает начальное семя n и значение r типа Rand a (который, помните, это просто синоним State StdGen a). Он создает новый генератор с mkStdGen n и подает его на evalState r. Функция evalState просто оценивает возвращаемое значение типа State s a, игнорируя состояние.

Опять же, мы можем преобразовать runRandIO в do нотации:

runRandIO :: Rand a -> IO a 
runRandIO r = do 
    rnd <- randomIO  -- generate a new random number using randomIO 
    return (runRand rnd r) -- use that number as the initial seed for runRand 

Наконец, getRandoms принимает число n, представляющее количество случайных значений, которые вы хотите создать. Он создает список [1..n] и применяет getRandom к списку. Обратите внимание, что фактические значения в [1..n] не используются (вы можете сказать, потому что функция лямбда начинается с \_ -> ...). В списке есть только что-то с правильным количеством элементов. Так как getRandom возвращает монадическое значение, мы используем mapM для сопоставления по списку, что приводит к правильному потоку состояния (то есть StdGen) через каждый из вызовов до getRandom.

5

Основная идея проста - для создания псевдослучайных чисел вам необходимо поддерживать некоторое состояние между вызовами функций. Таким образом, тип Rand a определяется как «a наряду с состоянием, необходимым для случайности».

Состояние сохраняется с использованием State monad. Это обеспечивает два основных действия: get и put, которые делают именно то, что они звучат. Таким образом, getRandom просто просматривает текущее состояние, а затем вызывает функцию random. Эта функция возвращает два значения: случайное значение и новое состояние. Затем вы только put новое состояние и оберните полученное значение.

runRand позволяет разворачивать «случайное» значение с учетом семени. evalState позволяет выполнить вычисление с учетом состояния (то есть значение типа State s a или, в данном случае, Rand a), заданное начальное состояние, а затем просто отбрасывает конечное состояние, давая результат. Таким образом, это позволяет запускать Rand a с заданным семенем и возвращает только полученное значение. Значение может иметь тип a, а не Rand a, потому что он всегда даст вам тот же результат для одного и того же семени.

runRandomIO просто делает то же самое, за исключением того, что семя основывается на некотором глобальном состоянии в IO.

getRandoms просто получает вам список Rand a значений по телефону getRandom для каждого элемента [1..n] списка (без учета фактического количества).

Смежные вопросы