Выбрать главу

Она принимает строку и запускает её в терминале. Так же как мы делали это с помощью приставки :! в

интерпретаторе. Значение типа ExitCode говорит о результате выполнения строки. Он может быть успешным,

тогда функция вернёт ExitSuccess и закончиться ошибкой, тогда мы сможем узнать код ошибки по значению

ExitFailure Int.

Случайные значения

Функции для создания случайных значений определены в модуле System.Random. Модуль System.Random

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

через интернет, набрав в командной строке cabal install random. Сначала давайте разберёмся как гене-

рируются случайные числа. Стандартные случайные числа очень похожи на те, что были у нас, когда мы

рассматривали примеры специальных функций. У нас есть генератор случайных чисел типа g и с помощью

функции next мы можем получить обновлённый генератор и случайное целое число:

next :: g -> (Int, g)

Не правда ли этот тип очень похож на тип результата функций с состоянием. В качестве состояния теперь

выступает генератор случайных чисел g. Это поведение описывается классом RandomGen:

class RandomGen g 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> 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, только теперь мы можем получать не только целые числа.