Функции для создания случайных значений определены в модуле System.Random
. Модуль System.Randomвходит в библиотеку random. Если в вашей поставке ghc его не оказалось, вы можете установить его вручную
через интернет, набрав в командной строке cabal install random. Сначала давайте разберёмся как гене-
рируются случайные числа. Стандартные случайные числа очень похожи на те, что были у нас, когда мы
рассматривали примеры специальных функций. У нас есть генератор случайных чисел типа g и с помощью
функции next мы можем получить обновлённый генератор и случайное целое число:
next ::
g -> (Int, g)Не правда ли этот тип очень похож на тип результата функций с состоянием. В качестве состояния теперь
выступает генератор случайных чисел g. Это поведение описывается классом RandomGen
:class RandomGen
g wherenext
::
g -> (Int, g)split
::
g -> (g, g)geтRange ::
g -> (Int, Int)Функция next обновляет генератор и возвращает случайное значение типа Int
. Функция split раска-лывает один генератор на два. Функция genRange возвращает диапазон значений генерируемых случайных
чисел. Первое значение в паре результата genRange должно быть всегда меньше второго. Для этого класса
определён один экземпляр, это тип StdGen
. Мы можем создать первый генератор по целому числу с помощьюфункции mkStdGen:
mkStdGen :: Int -> StdGen
Давайте посмотрим как это происходит в интерпретаторе:
Типичные задачи IO | 133
Prelude> :
m System.RandomPrelude System.Random> let
g0 = mkStdGen 0Prelude System.Random> let
(n0, g1) = next g0Prelude System.Random> let
(n1, g2) = next g1Prelude System.Random>
n02147482884
Prelude System.Random>
n12092764894
Мы создали первый генератор, а затем начали получать новые. Для того, чтобы получать новые случайные
числа, нам придётся таскать везде за собой генератор случайных чисел. Мы можем обернуть его в функцию
с состоянием и пользоваться методами классов 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 next1386438055
Prelude System.Random>
getStdRandom next961860614
И не надо обновлять никаких генераторов. Но вместо одного неудобства мы получили другое. Теперь
значение завёрнуто в оболочку IO
.Генератор StdGen
делает случайные числа из диапазона всех целых чисел. Что если мы хотим получитьтолько числа из некоторого интервала? И как получить случайные значения других типов? Для этого суще-
ствует класс Random
. Он является удобной надстройкой над классом RandomGen. Посмотрим на его основныеметоды:
class Random
a whererandomR :: 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]За счёт лени мы будем получать новые значения по мере необходимости.
randomRIO
::
(a, a) -> IO arandomIO
:: IO
aЭти функции выполняют тоже, что и основные функции класса, но им не нужен генератор случайных
чисел, они создают его с помощью функции getStdRandom. Экземпляры Random
определены для Bool, Char,Double
, Float, Int и Integer. Например так мы можем подбросить кости десять раз:134 | Глава 8: IO
Prelude System.Random>
fmap (take 10 . randomRs (1, 6)) getStdGen[5,6,5,5,6,4,6,4,4,4]
Prelude System.Random>
fmap (take 10 . randomRs (1, 6)) getStdGen