Случайность и монада State
В начале этого раздела мы говорили о том, что генерация случайных чисел может иногда быть неуклюжей. Каждая функция, использующая случайность, принимает генератор и возвращает случайное число вместе с новым генератором, который должен затем быть использован вместо прежнего, если нам нужно сгенерировать ещё одно случайное число. Монада State намного упрощает эти действия.
Функция random из модуля System.Random имеет следующий тип:
random :: (RandomGen g, Random a) => g –> (a, g)
Это значит, что она берёт генератор случайных чисел и производит случайное число вместе с новым генератором. Нам видно, что это вычисление с состоянием, поэтому мы можем обернуть его в конструктор newtype State при помощи функции state, а затем использовать его в качестве монадического значения, чтобы передача состояния обрабатывалась за нас:
import System.Random
import Control.Monad.State
randomSt :: (RandomGen g, Random a) => State g a
randomSt = state random
Поэтому теперь, если мы хотим подбросить три монеты (True – это «решка», а False – «орёл»), то просто делаем следующее:
import System.Random
import Control.Monad.State
threeCoins :: State StdGen (Bool, Bool, Bool)
threeCoins = do
a <– randomSt
b <– randomSt
c <– randomSt
return (a, b, c)
Функция threeCoins – это теперь вычисление с состоянием, и после получения исходного генератора случайных чисел она передаёт этот генератор в первый вызов функции randomSt, которая производит число и новый генератор, передаваемый следующей функции, и т. д. Мы используем выражение return (a, b, c), чтобы представить значение (a, b, c) как результат, не изменяя самый последний генератор. Давайте попробуем:
ghci> runState threeCoins (mkStdGen 33)
((True,False,True),680029187 2103410263)
Теперь выполнение всего, что требует сохранения некоторого состояния в промежутках между шагами, в самом деле стало доставлять значительно меньше хлопот!