haskell-notes

компьютером, мы запрашиваем глобальное для системы случайное значение, поэтому возвращаемое значе-

ние будет завёрнуто в тип 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]

За счёт лени мы будем получать новые значения по мере необходимости.

randomRIO

:: (a, a) -> IO a

randomIO

:: 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

[5,6,5,5,6,4,6,4,4,4]

Обратите внимание на то, что функция getStdGen не обновляет генератор случайных чисел. Мы запра-

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

Генератор будет обновляться, если воспользоваться функцией newStdGen:

Prelude System.Random> fmap (take 10 . randomRs (1, 6)) newStdGen

[1,1,5,6,5,2,5,5,5,3]

Prelude System.Random> fmap (take 10 . randomRs (1, 6)) newStdGen

[5,4,6,5,5,5,1,5,5,2]

Создадим случайные слова из пяти букв:

Prelude System.Random> fmap (take 5 . randomRs (’a’, ’z’)) newStdGen

”maclg”

Prelude System.Random> fmap (take 5 . randomRs (’a’, ’z’)) newStdGen

”nfjoa”

Цитатник

Напишем небольшую программу, которая будет выводить на экран в случайном порядке цитаты. Цитаты

хранятся в виде списка пар (автор, высказывание). Сначала мы генерируем случайное число в диапазоне

длины списка, затем выбираем цитату под этим номером и выводим её на экран.

module Main where

import Control.Applicative

import System.Random

main =

format . (quotes !! ) randomRIO (0, length quotes 1)

>>= putStrLn

format (a, b) = b

++ space ++ a ++ space

where space = ”nn”

quotes = [

(”Бьёрн Страуструп”,

”Есть лишь два вида языков программирования: те,

на которые вечно жалуются, и те, которые никогда

не используются.”),

(”Мохатма Ганди”, ”Ты должен быть теми изменениями, которые

ты хочешь видеть вокруг.”),

(”Сократ”, ”Я знаю лишь то, что ничего не знаю.”),

(”Китайская народная мудрость”, ”Сохранив спокойствие в минуту

гнева, вы можете избежать сотни дней сожалений”),

(”Жан Батист Мольер”, ”Медленно растущие деревья приносят лучшие плоды”),

(”Антуан де Сент-Экзюпери”, ”Жить это значит медленно рождаться”),

(”Альберт Эйнштейн”, ”Фантазия важнее знания.”),

(”Тони Хоар”, ”Внутри любой большой программы всегда есть

маленькая, что рвётся на свободу”),

(”Пифагор”, ”Не гоняйся за счастьем, оно всегда находится в тебе самом”),

(”Лао Цзы”, ”Путешествие в тысячу ли начинается с одного шага”)]

Функция format приводит цитату к виду приятному для чтения. Попробуем программу в интерпретаторе:

Prelude> :! ghc —make Quote -o hi

[1 of 1] Compiling Main

( Quote. hs, Quote. o )

Linking hi

Prelude> :! ./hi

Путешествие в тысячу ли начинается с одного шага

Лао Цзы

Типичные задачи IO | 135

Prelude> :! ./hi

Не гоняйся за счастьем, оно всегда находится в тебе самом

Пифагор

Исключения

Мы уже знаем несколько типов, с помощью которых функции могут сказать, что что-то случилось не

так. Это типы Maybe и Either. Если функции не удалось вычислить значение она возвращает специальное

значение Nothing или Left reason, по которому следующая функция может опознать ошибку и предпринять

какие-нибудь действия. Так обрабатываются ошибки в чистых функциях. В этом разделе мы узнаем о том,

Страницы: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162