Создадим генератор чуть иначе, не как в прошлый раз. Внутреннее состояние генератора вынесем наружу. Теперь генератор случайных значений получает на вход состояние, возвращает случайное значение и следующее состояние:
newtype State = State Integer deriving Show -- как data, но эффективней
newtype Random a = Random (State -> (a, State))
nextState :: Integer -> Integer
--nextState x0 = mod (134775813*x0 + 1) (2^32)
nextState x0 = mod (25214903917*x0 + 11) (2^48)
-- возвращает случайное значение и превращает старое состояние в новое
generate :: Random a -> State -> (a, State)
-- generate (Random f) state = f state
generate (Random f) = f
-- ползволяет сгенерировать N значений в списке из одного генератора.
generateN :: Random a -> Int -> State -> ([a], State)
generateN _ 0 state = ([], state)
generateN random n state = (h : t, finalState) where
(h, nextState) = generate random state
(t, finalState) = generateN random (n - 1) nextState
randomInRange :: Integer -> Integer -> Random Integer
randomInRange min max = Random $ \(State x0) -> (min + (x0 `div` (2^16) `mod` (max - min + 1)), State $ nextState x0)
-- тестируем генерацию чисел
random_1_10 = randomInRange 1 10
state = State 13434
let (x, nextState) = generateN random_1_10 10 state
x
nextState
[1,8,1,9,2,3,5,8,4,1]
State 144811957873548
Нужно сделать Random
монадой. Сначала сделаем Random
функтором:
instance Functor Random where
-- g :: State -> a, f :: a -> b
fmap f random = Random $ \state -> (let
(val, s2) = generate random state
in (f val, s2)
)
random_0_1 = randomInRange 0 1
data Coin = Head | Tail deriving Show
genHeadTail = (\x -> if x == 0 then Head else Tail) <$> random_0_1
generateN genHeadTail 10 (State 1244)
generateN random_0_1 10 (State 1244)
generate genHeadTail (State 123)
generate random_0_1 (State 123)
([Head,Tail,Head,Head,Head,Tail,Head,Tail,Head,Tail],State 147328094030846)
([0,1,0,0,0,1,0,1,0,1],State 147328094030846)
(Head,State 3101433181802)
(0,State 3101433181802)
Следующий шаг, сделаем Random
аппликативным функтором.
<*> :: Random (a -> b) -> Random a -> Random b
instance Applicative Random where
-- делает из обычного значения случайное, это генератор всегда одного значения
pure x = Random (\state -> (x, state))
randomF <*> randomVal = Random (\state -> let
(f, state2) = generate randomF state
(val, state3) = generate randomVal state2
in (f val, state3)
)
Проверяем сумму двух случайных величин. Необходмо сделать import
, чтобы заработал liftA2
.
import Control.Applicative
random10plus10 = liftA2 (+) random_1_10 random_1_10
-- или random10plus10 = (+) <$> random_1_10 <*> random_1_10
generateN random10plus10 100 (State 1)
([10,18,7,13,15,11,9,12,19,10,7,15,9,8,10,12,6,9,4,16,10,14,10,5,12,12,4,12,10,14,13,9,12,10,8,5,7,15,7,16,18,10,10,9,10,8,9,14,17,17,8,11,5,15,9,18,12,10,9,11,18,14,10,18,16,4,12,16,11,7,15,15,14,9,13,11,7,6,4,13,12,18,17,5,15,13,15,11,7,12,13,11,3,11,6,9,7,7,3,17],State 122086591259433)
Последний шаг, делаем Random
монадой:
instance Monad Random where
random >>= f = Random (\state -> let
(randomVal, state2) = generate random state
(val, nextState) = generate (f randomVal) state2
in (val, nextState))
Создаем генератор, который складывает три случайных значения 0 или 1:
r = do
a <- randomInRange 0 1
b <- randomInRange 0 1
c <- randomInRange 0 1
return $ a + b + c
generateN r 100 (State 2)
([2,2,1,2,2,1,3,1,2,1,2,1,2,1,3,3,1,0,1,1,0,1,0,3,2,2,2,3,1,1,1,2,2,2,1,1,3,2,1,3,1,0,0,2,3,1,2,2,1,2,1,3,2,2,2,1,1,0,1,0,2,1,0,1,0,0,2,1,1,1,1,1,2,2,1,2,0,2,1,3,2,2,1,3,1,1,0,2,1,1,3,3,1,3,0,2,2,2,2,0],State 100669495570254)