Есть и дополнительные методы. Есть методы, которые позволяют генерировать список всех возможных
случайных значений для данного генератора:
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 = ”\n\n”
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, по которому следующая функция может опознать ошибку и предпринять
какие-нибудь действия. Так обрабатываются ошибки в чистых функциях. В этом разделе мы узнаем о том,
как обрабатываются ошибки, которые происходят при взаимодействии с внешним миром, ошибки, которые
происходят внутри типа IO.
Ошибки функций с побочными эффектами обрабатываются с помощью специальной функции catch, она
определена в Prelude:
catch :: IO a -> (IOError -> IO a) -> IO a
Эта функция принимает значение, которое содержит побочные эффекты и функцию, которая обрабаты-
вает исключительные ситуации. К примеру если мы попытаемся прочитать данные из файла, к которому у