Я пытаюсь понять Государственную Монаду, и с этой целью я хотел написать монадический код, который генерировал бы последовательность случайных чисел с использованием линейного конгруэнтного генератора (вероятно, это не очень хорошо, но я намерен просто изучить состояние Monad, не создайте хорошую библиотеку RNG).
Генератор именно это (я хочу создать последовательность Bool
для простоты):
type Seed = Int
random :: Seed -> (Bool, Seed)
random seed = let (a, c, m) = (1664525, 1013904223, 2^32) -- some params for the LCG
seed' = (a*seed + c) `mod` m
in (even seed', seed') -- return True/False if seed' is even/odd
Не беспокойтесь о числах, это просто правило обновления для семени, которое (согласно Numerical Recipes) должно генерировать псевдослучайную последовательность Int
s. Теперь, если я хочу генерировать случайные числа последовательно, я бы сделал:
rand3Bools :: Seed -> ([Bool], Seed)
rand3Bools seed0 = let (b1, seed1) = random seed0
(b2, seed2) = random seed1
(b3, seed3) = random seed2
in ([b1,b2,b3], seed3)
Хорошо, поэтому я мог избежать этого шаблона, используя State Monad:
import Control.Monad.State
data Random {seed :: Seed, value :: Bool}
nextVal = do
Random seed val <- get
let seed' = updateSeed seed
val' = even seed'
put (Random seed' val')
return val'
updateSeed seed = let (a,b,m) = (1664525, 1013904223, 2^32) in (a*seed + c) `mod` m
И наконец:
getNRandSt n = replicateM n nextVal
getNRand :: Int -> Seed -> [Bool]
getNRand n seed = evalState (getNRandStates n) (Random seed True)
Хорошо, это прекрасно работает и дает мне список n псевдослучайных Bool
для каждого заданного семени. Но...
Я могу прочитать, что я сделал (в основном на основе этого примера: http://www.haskell.org/pipermail/beginners/2008-September/000275.html) и повторить его, чтобы делать другие вещи. Но я не думаю, что могу понять, что действительно происходит за do-notation и монадическими функциями (например, replicateM).
Может кто-нибудь помочь мне с некоторыми из этих сомнений?
1 - Я попытался отменить функцию nextVal, чтобы понять, что он делает, но я не мог. Я могу догадаться, что он извлекает текущее состояние, обновляет его, а затем передает состояние вперед следующему вычислению, но это только на основе чтения этого do-sugar, как будто это был английский.
Как мне по-настоящему отбросить эту функцию до исходных → = и возвращать функции шаг за шагом?
2 - Я не мог понять, что именно делают функции put
и get
. Я могу догадаться, что они "упаковывают" и "распаковывают" состояние. Но механика, стоящая за до-сахаром, все еще неуловима для меня.
Хорошо, любые другие общие замечания об этом коде очень приветствуются. Я иногда падал с Haskell, что могу создать код, который работает, и делать то, что я ожидаю от него, но я не могу "следить за оценкой", как я привык делать с императивными программами.