haskell-notes

1:

addRandom :: Double -> Random Double

addRandom x = fmap (+x) next

Посмотрим как эта функция работает в интерпретаторе:

*Random> runState (addRandom 5) 0.5

(5.5,0.9735000000000014)

*Random> runState (addRandom 5) 0.7

(5.7,0.16289999999999338)

*Random> runState (mapM addRandom [1 .. 5]) 0.5

([1.5,2.9735000000000014,3.139404500000154,4.769488561516319,

5.5250046269694195],0.6226652135290891)

В последней строчке мы с помощью функции mapM прибавили ко всем элементам списка разные случайные

числа, обновление счётчика происходило за кадром, с помощью функции mapM и экземпляра Monad для State.

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

[1+a, 1+a], а другое из интервала [2+b,2+b]:

addRandom2 :: Double -> Double -> Random Double

addRandom2 a b = liftA2 add next next

where add

a b = x y -> diap a 1 x + diap b 1 y

diap c r = x

-> x * 2 * r r + c

Функция diap перемещает интервал от 0 до 1 в интервал от cr до c+r. Обратите внимание на то как мы

сначала составили обычную функцию add, которая перемещает значения из интервала от 0 до 1 в нужный

диапазон и складывает. И только в самый последний момент мы применили к этой функции случайные

значения. Посмотрим как работает эта функция:

*Random> runState (addRandom2 0 10) 0.5

(10.947000000000003,0.13940450000015403)

*Random> runState (addRandom2 0 10) 0.7

(9.725799999999987,0.2587662999992979)

Прибавим два списка и получим сумму:

*Random> let res = fmap sum $ zipWithM addRandom2 [1.. 3] [11 .. 13]

*Random> runState res 0.5

(43.060125804029965,0.969511377766409)

*Random> runState res 0.7

(39.86034841613788,0.26599261421101517)

Функция zipWithM является аналогом функции zipWith. Она устроена также как и функция mapM, сначала

применяется обычная функция zipWith, а затем функция sequence.

С помощью типа Random мы можем определить функцию подбрасывания монетки:

Случайные числа | 107

data Coin = Heads | Tails

deriving (Show)

dropCoin :: Random Coin

dropCoin = fmap drop’ next

where drop’ x

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

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. Посмотрим

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

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