Мы будем обновлять значения уровня громкости не напрямую, а с помощью вспомогательных функций
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).