52700.fb2
Типичные задачи IO | 133
Prelude> :m System.Random
Prelude System.Random> let g0 = mkStdGen 0
Prelude System.Random> let (n0, g1) = next g0
Prelude System.Random> let (n1, g2) = next g1
Prelude System.Random> n0
2147482884
Prelude System.Random> n1
2092764894
Мы создали первый генератор, а затем начали получать новые. Для того, чтобы получать новые случайные
числа, нам придётся таскать везде за собой генератор случайных чисел. Мы можем обернуть его в функцию
с состоянием и пользоваться методами классов Functor, Applicative и Monad. Обновление генератора будет
происходить за ширмой, во время применения функций. Но у нас есть и другой путь.
Вместо монады State мы можем воспользоваться монадой IO. Если нам лень определять генератор слу-
чайных чисел, мы можем попросить компьютер определить его за нас. В этом случае мы взаимодействуем с
компьютером, мы запрашиваем глобальное для системы случайное значение, поэтому возвращаемое значе-
ние будет завёрнуто в тип IO. Для этого определены функции:
getStdGen :: IO StdGen
newStdGen :: IO StdGen
Функция getStdGen запрашивает глобальный для системы генератор случайных чисел. Функция
newStdGen не только запрашивает генератор, но также и обновляет его. Мы пользуемся этими функци-
ями так же как и mkStdGen, только теперь мы спрашиваем первый аргумент у компьютера, а не передаём его
вручную. Также есть ещё одна полезная функция:
getStdRandom
:: (StdGen -> (a, StdGen)) -> IO a
Посмотрим, что получится, если передать в неё функцию next:
Prelude System.Random> getStdRandom next
1386438055
Prelude System.Random> getStdRandom next
961860614
И не надо обновлять никаких генераторов. Но вместо одного неудобства мы получили другое. Теперь
значение завёрнуто в оболочку IO.
Генератор StdGen делает случайные числа из диапазона всех целых чисел. Что если мы хотим получить
только числа из некоторого интервала? И как получить случайные значения других типов? Для этого суще-
ствует класс Random. Он является удобной надстройкой над классом RandomGen. Посмотрим на его основные
методы:
class Random a where
randomR :: RandomGen g => (a, a) -> g -> (a, g)
random
:: RandomGen g => g -> (a, g)
Метод randomR принимает диапазон значений, генератор случайных чисел и возвращает случайное число
из указанного диапазона и обновлённый генератор. Метод random является синонимом метода next из класса
RandomGen, только теперь мы можем получать не только целые числа.
Есть и дополнительные методы. Есть методы, которые позволяют генерировать список всех возможных
случайных значений для данного генератора:
randomRs :: RandomGen g => (a, a) -> g -> [a]
randoms
:: RandomGen g => g -> [a]
За счёт лени мы будем получать новые значения по мере необходимости.