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

louder и quieter. Так мы не сможем выйти за пределы заданного диапазона.

Возможные события:

108 | Глава 7: Функторы и монады: примеры

data User = Button | Quieter | Louder

deriving (Show)

Пользователь может либо нажать на кнопку вкл/выкл или повернуть реле громкости влево, чтобы при-

глушить звук (Quieter) или вправо, чтобы сделать погромче (Louder). Будем считать, что колонки всегда

включены в розетку.

Составим функцию переходов:

speaker :: User -> FSM Speaker

speaker = fsm $ trans

where trans Button

(Sleep, n) = (Work, n)

trans Button

(Work,

n) = (Sleep, n)

trans Louder

(s,

n) = (s, louder n)

trans Quieter

(s,

n) = (s, quieter n)

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

включении они будут работать на той же громкости. Реле можно крутить и в состоянии Sleep. Посмотрим

на типичную сессию работы колонок:

*FSM> let res = mapM speaker [Button, Louder, Quieter, Quieter, Button]

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

ключаем. Посмотрим что получилось:

*FSM> runState res (Sleep, Level 2)

([(Sleep, Level 2),(Work, Level 2),(Work, Level 3),(Work, Level 2),

(Work, Level 1)],(Sleep, Level 1))

*FSM> runState res (Sleep, Level 0)

([(Sleep, Level 0),(Work, Level 0),(Work, Level 1),(Work, Level 0),

(Work, Level 0)],(Sleep, Level 0))

Смотрите, изменив начальное значение, мы изменили весь список значений. Обратите внимание на то,

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

установленный предел.

Определим колонки другого типа. Наши новые колонки будут безопаснее предыдущих. Представьте си-

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

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

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

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

Колонки при выключении будут выставлять уровень громкости на ноль и реле можно будет крутить

только если колонки включены.

safeSpeaker :: User -> FSM Speaker

safeSpeaker = fsm $ trans

where trans Button

(Sleep, _) = (Work,

Level 0)

trans Button

(Work,

_) = (Sleep, Level 0)

trans Quieter (Work,

n) = (Work,

quieter n)

trans Louder

(Work,

n) = (Work,

louder n)

trans _

(Sleep, n) = (Sleep, n)

При нажатии на кнопку вкл/выкл уровень громкости выводится в положение 0. Колонки реагируют на

запросы изменения уровня громкости только в состоянии Work. Посмотрим как работают наши новые колон-

ки:

*FSM> let res = mapM safeSpeaker [Button, Louder, Quieter, Button, Louder]

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

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

громкости 10:

*FSM> runState res (Sleep, Level 10)

([(Sleep, Level 10),(Work, Level 0),(Work, Level 1),(Work, Level 0),

(Sleep, Level 0)],(Sleep, Level 0))

Конечные автоматы | 109

Первое значение в списке является стартовым состоянием, которое мы задали. После этого колонки вклю-

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

ляем её и выключаем. Попытка изменить громкость выключенных колонок не проходит. Это видно по по-

следнему элементу списка и итоговому состоянию колонок, которое находится во втором элементе пары.

Предположим, что колонки работают с самого начала, тогда первым действием мы выключаем их. По-

смотрим, что случится дальше:

*FSM> runState res (Work, Level 10)

([(Work, Level 10),(Sleep, Level 0),(Sleep, Level 0),(Sleep, Level 0),

(Work, Level 0)],(Work, Level 1))

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

7.3 Отложенное вычисление выражений

В этом примере мы будем выполнять арифметические операции на целых числах. Мы будем их скла-

дывать, вычитать и умножать. Но вместо того, чтобы сразу вычислять выражения мы будем составлять их

описание. Мы будем кодировать операции конструкторами.

data Exp

= Var String

| Lit Int

| Neg Exp

| Add Exp Exp

| Mul Exp Exp

deriving (Show, Eq)

У нас есть тип Exp, который может быть либо переменной Var с данным строчным именем, либо целочис-

ленной константой Lit, либо одной из трёх операций: вычитанием (Neg), сложением (Add) или умножением

(Mul).

Перейти на страницу:

Похожие книги