Выбрать главу

| x < 0.5

= Heads

| otherwise = Tails

У монетки две стороны орёл (Heads) и решка (Tails). Поскольку шансы на выпадание той или иной

стороны равны, мы для определения стороны разделяем интервал от 0 до 1 в равных пропорциях.

Подбросим монетку пять раз:

*Random> let res = sequence $ replicate 5 dropCoin

Функция replicate n a составляет список из n повторяющихся элементов a. Посмотрим что у нас полу-

чилось:

*Random> runState res 0.4

([Heads, Heads, Heads, Heads, Tails],0.5184926967068364)

*Random> runState res 0.5

([Tails, Tails, Heads, Tails, Tails],0.6226652135290891)

7.2 Конечные автоматы

С помощью монады State можно описывать конечные автоматы (finite-state machine). Конечный автомат

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

за другим. На каждое событие автомат реагирует переходом из одного состояния в другое.

type FSM s = State s s

fsm :: (ev -> s -> s) -> (ev -> FSM s)

fsm transition = \e -> State $ \s -> (s, transition e s)

Функция fsm принимает функцию переходов состояний transition и возвращает функцию, которая при-

нимает состояние и возвращает конечный автомат. В качестве значения конечный автомат FSM будет возвра-

щать текущее состояние.

С помощью конечных автоматов можно описывать различные устройства. Лентой событий будет ввод

пользователя (нажатие на кнопки, включение/выключение питания).

Приведём простой пример. Рассмотрим колонки, у них есть розетка, кнопка вкл/выкл и регулятор гром-

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

type Speaker = (SpeakerState, Level)

data SpeakerState = Sleep | Work

deriving (Show)

data Level

= Level Int

deriving (Show)

Тип колонок складывается из двух значений: состояния и уровня громкости. Колонки могут быть вы-

ключенными (Sleep) или работать на определённой громкости (Work). Считаем, что максимальный уровень

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

такими функциями:

quieter :: Level -> Level

quieter (Level n) = Level $ max 0 (n-1)

louder :: Level -> Level

louder (Level n) = Level $ min 10 (n+1)

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

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))

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

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

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

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