Мы узнали как в Haskell обстоят дела с такими типичными задачами мира побочных эффектов как
ввод/вывод, чтение/запись файлов, генерация случайных значений, выполнение внешних программ, ини-
циализация программ с помощью флагов. Также мы узнали о том, как обрабатываются специфические для
типа IOисключения.
140 | Глава 8: IO
8.8 Упражнения
Старайтесь свести присутствие функций с побочными эффектами к минимуму. Идеальный случай, когда
тип IOвстречается только в функции main. Часто программы устроены более хитрым образом и функции
с побочными эффектами пытаются расползтись по всему коду. Но даже в этом случае программу можно
разделить на две части: в одной живут подлинные источники побочных эффектов, такие как чтение файлов,
генерация случайных значений, а в другой – чистые функции. Старайтесь устроить программу так, чтобы
она была максимально чистой. Чистые функции гораздо проще комбинировать, понимать, изменять.
• Это упражнение даёт вам возможность почувствовать преимущества чистого кода. Вспомните функ-
цию поиска корней методом неподвижной точки (этот пример встречался в главе о ленивых вычисле-
ниях). Напишите на основе этого примера программу, которая будет распечатывать решение и после-
довательность приближений. Последовательность приближений состоит из текущего значения корня
и расстоянии между корнями.
Напишите два варианта программы, в одном вы измените алгоритм так, чтобы печать происходила
одновременно с вычислениями (не пользуясь функцией из модуля Debug.Trace). А в другом вариан-
те алгоритм останется прежним. Но теперь вместо решения найдите список первых приближений до
решения. А затем передайте его в отдельную функцию печати результатов.
В первом варианте алгоритм смешан с печатью. А во втором программа распадается на две части, часть
вычислений и часть вывода результатов на экран. Сравните два подхода.
• Напишите программу для угадывания чисел. Компьютер загадал число в заданном диапазоне и про-
сит вас угадать его. Если вы ошибаетесь он подсказывает: “холодно-горячо” или “больше-меньше”.
Программа принимает два аргумента, которые определяют диапазон возможных значений для неиз-
вестного числа.
• С помощью стандартных функций для генерации случайных чисел напишите программу, которая про-
водит состязание по игре в кости. Программа принимает аргументом суммарное число очков необходи-
мых для победы. Двое игроков бросают по очереди кости побеждает тот, кто первым наберёт заданную
сумму.
Сделайте так чтобы результаты выводились постепенно. С каждым нажатием на Enterвы подбрасы-
ваете кости (два шестигранных кубика). После каждого раунда программа выводит промежуточные
результаты.
• Напишите программу, которая принимает два аргумента: набор слов разделённых пробелами и файл.
А выводит она строки файла, в которых встречается данное слово.
Воспользуйтесь стандартными функциями из модуля Data.List
-- разбиение строки на подстроки по переносам каретки
lines :: String ->[ String]
-- разбиение строки на подстроки по пробелам
words :: String ->[ String]
-- возвращает True только в том случае, если
-- первый список полностью содержится во втором
isInfixOf :: Eqa =>[a] ->[a] -> Bool
• Классы Functorи Applicativeзамкнуты относительно композиции. Это свойство говорит о том, что
композиция (аппликативных) функторов снова является (аппликативным) функтором. Докажите это!
Пусть дан тип, который описывает композицию двух типов:
newtype Of g a = O{ unO ::f (g a) }
Определите экземпляры классов:
instance( Functorf, Functorg) => Functor( Of g) where ...
instance( Applicativef, Applicativeg) => Applicative( Of g) where ...
Подсказка: если совсем не получается, ответ можно подсмотреть в библиотеке TypeCompose. Но пока мы
не знаем как устанавливать библиотеки и где они живут, всё-таки попытайтесь решить это упражнение
самостоятельно.
Упражнения | 141
Глава 9
Редукция выражений
В этой главе мы поговорим о том как вычисляются программы. В самом начале мы говорили о том, что
процесса вычисления значений нет. В том смысле, что у нас нет новых значений, у нас ничего не меняется,
Читать дальше