из модуля >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)
Теперь выполнение всего, что требует сохранения некоторого состояния в промежутках между шагами, в самом деле стало доставлять значительно меньше хлопот!