haskell-notes

drawWorld :: World -> IO ()

drawWorld = draw . picture . worldPure

— в Graphics.hs

draw :: Picture -> IO ()

— в Pure.hs

picture

:: Pure -> Picture

Добавим функцию инициализации игры:

initWorld :: IO World

initWorld = do

dirty

<- initDirty

(sense, events) <- percept dirty

return $ World (initPure sense events) dirty

— в Dirty.hs

initDirty :: IO Dirty

— в Pure.hs

initPure :: Sense -> [Event] -> Pure

20.6 Детализируем дальше

Вот так на самом интересном месте… Мы вынуждены прерваться. Я надеюсь, что вы уловили основную

идею метода и сможете закончить эту игру самостоятельно. Вся логика игры будет описана в модуле Pure. hs.

Причём в этом модуле будут только чистые функции. Осталось примерно 1000 строк кода. Я не буду выпи-

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

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

20.7 Краткое содержание

В этой главе мы посмотрели на две интересные библиотеки. Физический движок Hipmunk и графическую

библиотеку OpenGL и узнали метод укрощения императивного кода. Мы разделили состояние игры на две

части. В одну поместили все те параметры, для которых невозможно обойтись без IO-функций, а в другой

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

являются чистыми. Параметры императивной части не обновляются сразу, сначала мы делаем с них снимок,

потом передаём этот снимок в чистую часть, и она разбирается с тем как их обновлять. Части общаются между

собой на специальных маленьких языках, которые закодированы в типах. Это язык наблюдений (Event), язык

реакций (Query) и язык отрисовки игрового мира (Picture).

20.8 Упражнения

Закончите код игры. Или, возможно, при знакомстве с Hipmunk у вас появилась идея новой игры с неве-

роятной динамикой. Ещё лучше! Напишите её. При этом продумайте проект игры так, чтобы IO-типы не

разбежались по всей программе.

304 | Глава 20: Императивное программирование

Глава 21

Музыкальный пример

В этой главе мы напишем музыкальный секвенсор. Мы будем переводить нотную запись в midi-файл с

помощью библиотеки HCodecs. Она предоставляет возможность создания midi-файлов по описанию в Haskell.

При этом описание напоминает описание самого формата midi. Мы же хотим подняться уровнем выше и

описывать музыку нотами и композицией нот.

21.1 Музыкальная нотация

Для начала зададимся выясним: а что же такое музыка с точки зрения нашего секвенсора? Мы ищем

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

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

это midi-файл. Так например мы можем сразу отбросить представление в виде сигналов, последовательности

сэмплов, поскольку мы не сможем реализовать это представление в рамках midi. За ответом обратимся к

истории.

Нотная запись в европейской традиции

В европейской традиции принято описывать музыку в виде нотной записи. Нотный лист состоит из серии

нотных станов. Нотный стан состоит из пяти линеек. Каждая линейка обозначает определённую высоту. Нота

состоит из обозначения длительности и высоты. Разные длительности обозначаются штрихами и цветом

ноты, а высоте соответствует расположение на нотном стане.

Рис. 21.1: Буквенные обозначения высоты ноты

По длительности ноты различают на: целые, половины, четверти, восьмые, шестнадцатые и так далее.

Каждая последующая длительность в два раза меньше предыдущей. Длительность измеряется в долях от

такта. Такты обозначаются сплошной линией, которая перечёркивает все пять линеек нотного стана. По

высоте ноты, зависят от двух целых чисел, это номер октавы и номер ступени лада. В ладе обычно всего 12

ступеней. Их обозначают разными именами. Например в латинской нотации их обозначают так:

0

1

2

3

4

5

6

7

8

9

10

11

C

C

D

D

E

F

F

G

G

A

A

B

C

D

D

E

E

F

G

G

A

A

B

B

do

re

mi

f a

sol

la

ti

В самом нижнем ряду расположены имена нот. Во втором и четвёртом – обозначения нот с диезами и

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

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

диез или понижением на один шаг с помощью знака бемоль b.

| 305

Также ноты различают по громкости. В европейской традиции считается, что громкость изменяется не

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

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

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

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

Теперь давайте посмотрим крупным планом на протокол midi.

Протокол midi

Протокол midi появился в ответ на бурное развитие синтезаторов. Каждый из синтезаторов предлагал

свои тембры, при этом люди задумались, а нужна ли синтезатору клавиатура? Вопрос кажется абсурдным,

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

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

Страницы: 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