из интервала от 0 до 1 с помощью алгоритма:
nextRandom :: Double -> Double
nextRandom =snd .properFraction .(105.947 *)
Функция properFraction возвращает пару, которая состоит из целой части и остатка числа. Взяв второй
элемент пары с помощью snd, мы выделяем остаток. Функция nextRandom представляет собой генератор
случайных чисел, который принимает значение с предыдущего шага и строит по нему следующее значение.
Построим тип для случайных чисел:
type Randoma = State Doublea
next :: Random Double
next = State $\s ->(s, nextRandom s)
Теперь определим функцию, которая прибавляет к данному числу случайное число из интервала от 0 до
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
whereadd
a b =\x y ->diap a 1 x +diap b 1 y
diap c r =\x
->x *2 *r -r +c
Функция diap перемещает интервал от 0 до 1 в интервал от c -r до 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> letres =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
wheredrop’ x
|x <0.5
= Heads
|otherwise = Tails
У монетки две стороны орёл ( Heads) и решка ( Tails). Поскольку шансы на выпадание той или иной
стороны равны, мы для определения стороны разделяем интервал от 0 до 1 в равных пропорциях.
Подбросим монетку пять раз:
*Random> letres =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 FSMs = States s
fsm ::(ev ->s ->s) ->(ev -> FSMs)
fsm transition =\e -> State $\s ->(s, transition e s)
Функция fsm принимает функцию переходов состояний transition и возвращает функцию, которая при-
нимает состояние и возвращает конечный автомат. В качестве значения конечный автомат FSMбудет возвра-
щать текущее состояние.
С помощью конечных автоматов можно описывать различные устройства. Лентой событий будет ввод
пользователя (нажатие на кнопки, включение/выключение питания).
Приведём простой пример. Рассмотрим колонки, у них есть розетка, кнопка вкл/выкл и регулятор гром-
Читать дальше