52700.fb2 Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 226

Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 226

подвыражения мы можем оставить на потом и заняться другими. А потом, когда мы их доделаем может вдруг

оказаться, что это сложное выражение нам и не нужно.

Рис. 13.2: Дерево задач

Стратегия написания программ | 203

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

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

времени. И на остальные задачи у нас не хватит сил или мы можем потратить много времени на решение

задачи, которая совсем не нужна для итогового решения. Также как и в вычислениях по значению, мы можем

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

малая часть.

Ещё один плюс решения сверху вниз состоит в экономии усилий. Мы можем написать всю программу в

виде функций, которые состоят лишь из определений типов. И утрясти общую схему программы на типах.

Также при реализации отдельных частей программы, мы можем воспользоваться упрощёнными алгорит-

мами, достаточными для тестирования приложения, оставив отрисовку деталей на потом. Мы не тратим

время на реализацию, а смотрим как программа выглядит “вцелом”. Если общий набросок нас устраивает

мы можем начать заполнять дыры и детализировать отдельные выражения. Так мы будем детализировать-

детализировать пока не придём к первоначальному решению. Далее если у нас останется время мы можем

сменить реализацию некоторых частей. Но общая схема останется прежней, она уже устоялась на уровне ти-

пов. Часто такую стратегию разработки называют разработкой через прототипы (developing by prototyping).

При этом процесс написания приложения можно представить как процесс сходимости, приближения к преде-

лу. У нас есть серия промежуточных решений или прототипов, которые с каждым шагом всё точнее и точнее

описывают итоговую программу. Также если мы работаем в команде, то дробление задачи на подзадачи про-

исходит естественно, в ходе детализации, мы можем распределить нагрузку, распределив разные undefined

между участниками проекта.

Слово undefined будет встречаться очень часто, буквально в каждом значении. Оно очень длинное, и

часто писать его будет слишком утомительно. Определим удобный синоним. Я обычно использую un или

lol (что-нибудь краткое и удобное для автоматического поиска):

un :: a

un = undefined

Но давайте приступим к реализации нашей игры. Самая верхняя функция, будет запускать программу.

Назовём её play. Это функция взаимодействия с пользователем она ведёт диалог, поэтому её тип будет IO

():

play :: IO ()

play = un

Итак у нас появилась корневая функция. Что мы будем в ней делать? Для начала мы поприветствуем игро-

ка (функция greetings). Затем предложим ему начать игру (функция setup), после чего запустим цикл игры

(функция gameLoop). Приветствие это просто надпись на экране, поэтому тип у него будет IO (). Предложе-

ние игры вернёт стартовую позицию для игры, поэтому тип будет IO Game. Цикл игры принимает состояние

и продолжает диалог. В типах это выражается так:

play :: IO ()

play = greetings >> setup >>= gameLoop

greetings :: IO ()

greetings = un

setup :: IO Game

setup = un

gameLoop :: Game -> IO ()

gameLoop = un

Сохраним эти определения в модуле Loop и загрузим модуль с программой в интерпретатор:

Prelude> :l Loop

[1 of 2] Compiling Game