функции next мы можем получить обновлённый генератор и случайное целое число:
next ::g ->( Int, g)
Не правда ли этот тип очень похож на тип результата функций с состоянием. В качестве состояния теперь
выступает генератор случайных чисел g. Это поведение описывается классом RandomGen:
class RandomGeng where
next
::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.Random
Prelude System.Random> letg0 =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)) -> IOa
Посмотрим, что получится, если передать в неё функцию next:
Prelude System.Random>getStdRandom next
1386438055
Prelude System.Random>getStdRandom next
961860614
И не надо обновлять никаких генераторов. Но вместо одного неудобства мы получили другое. Теперь
значение завёрнуто в оболочку IO.
Генератор StdGenделает случайные числа из диапазона всех целых чисел. Что если мы хотим получить
только числа из некоторого интервала? И как получить случайные значения других типов? Для этого суще-
ствует класс Random. Он является удобной надстройкой над классом RandomGen. Посмотрим на его основные
методы:
class Randoma where
randomR :: RandomGeng =>(a, a) ->g ->(a, g)
random
:: RandomGeng =>g ->(a, g)
Метод randomR принимает диапазон значений, генератор случайных чисел и возвращает случайное число
из указанного диапазона и обновлённый генератор. Метод random является синонимом метода next из класса
RandomGen, только теперь мы можем получать не только целые числа.
Есть и дополнительные методы. Есть методы, которые позволяют генерировать список всех возможных
случайных значений для данного генератора:
randomRs :: RandomGeng =>(a, a) ->g ->[a]
randoms
:: RandomGeng =>g ->[a]
За счёт лени мы будем получать новые значения по мере необходимости.
randomRIO
::(a, a) -> IOa
randomIO
:: IOa
Эти функции выполняют тоже, что и основные функции класса, но им не нужен генератор случайных
чисел, они создают его с помощью функции getStdRandom. Экземпляры Randomопределены для Bool, Char,
Double, Float, Intи Integer. Например так мы можем подбросить кости десять раз:
134 | Глава 8: IO
Prelude System.Random>fmap (take 10 .randomRs (1, 6)) getStdGen
Читать дальше