State Monad - RNG

Run Settings
LanguageHaskell
Language Version
Run Command
-- 2024-09-06 - Emilio Francesquini <e.francesquini@ufabc.edu.br> -- Universidade Federal do ABC -- CC-BY-SA 4.0 module Main where import Data.Bits import Control.Monad ----------------------------------- -- Gerador de números aleatórios -- ----------------------------------- type RandomState = (Int, Int) simpleRandomInit :: Int -> RandomState simpleRandomInit seed = (z, w) where n1 = (seed * 48947) `mod` 4294967296; z = if n1 /= 9 then n1 else 362436069 n2 = (seed * 104623) `mod` 4294967296; w = if n2 /= 0 then n2 else 521288629 -- Este simples gerador de números aleatórios é baseado naquele -- proposto por George Marsaglia (MWC - multiply with carry). Apesar -- de ser muito simples, ele é capaz de passar pela série de testes -- Marsaglia's DIEHARD para geradores de números aleatórios. A -- implementação abaixo é uma adaptação daquela feita por John -- D. Cook. nextInt :: RandomState -> (Int, RandomState) nextInt (z, w) = (u, (z', w')) where z' = 36969 * (z .&. 65535) + (z `shiftR` 16) w' = 18000 * (w .&. 65535) + (w `shiftR` 16); u = (z `shiftL` 16) + w; --------------------------- -- Utilização aleatórios -- --------------------------- -- Usa o gerador como função pura randomList :: Int -> [Int] randomList seed = let s0 = simpleRandomInit seed (v0, s1) = nextInt s0 -- precisa carregar o estado (v1, s2) = nextInt s1 -- entre cada uma das chamadas (v2, _) = nextInt s2 in [v0, v1, v2] randomListSize :: Int -> Int -> [Int] randomListSize seed n = -- Para carregar o estado para um número arbitrário de chamadas usa -- um fold. Poderia ser também uma versão recursiva. reverse . fst $ foldl (\(res, s) _ -> let (v, s') = nextInt s in (v:res, s')) ([], simpleRandomInit seed) [1..n] -- Transformador de estados -- dado um estado inicial, devolve um novo estado e um valor newtype ST s a = ST (s -> (a, s)) -- Roda o state transformer com o estado (ambos recebidos por -- parâmetro) e devolve uma tupla com o resultado da execução e o novo -- estado rodaCom :: ST s a -> s -> (a, s) rodaCom (ST f) s0 = f s0 -- Veja slides da aula para maiores explicações instance Functor (ST s) where -- fmap :: (a -> b) -> ST a -> ST b -- x :: a fmap g st = ST stb where stb s = (g x, s') where (x, s') = rodaCom st s -- Veja slides da aula para maiores explicações instance Applicative (ST s) where -- <*> :: ST (a -> b) -> ST a -> ST b stf <*> stx = ST stb where stb s = (f x, s'') where (f, s') = rodaCom stf s (x, s'') = rodaCom stx s' pure x = ST (\s -> (x, s)) -- Veja slides da aula para maiores explicações instance Monad (ST s) where -- (>>=) :: ST a -> (a -> ST b) -> ST b st >>= f = ST stb where stb s = rodaCom (f x) s' where (x, s') = rodaCom st s -- Quero uma função que dado um estado me devolva um aleatório + -- estado alterado nextIntM :: ST RandomState Int nextIntM = ST nextInt -- Devolve um transformador de estados ST [Int] que quando executado -- com a função rodaCom devolverá ([Int], State) Para ser justo (e -- comparável com a função randomList) não usa mapM. A próxima função -- corrige isto. randomListM' :: ST RandomState [Int] randomListM' = do v0 <- nextIntM v1 <- nextIntM v2 <- nextIntM return [v0, v1, v2] randomListM :: Int -> [Int] randomListM seed = -- - rodaCom roda o transformador de estados randomList2' tendo como -- estado inicial o resultado de (simpleRandomInit seed) -- - fst "tira" o resultado do (a, State) = Valor + contexto fst $ rodaCom randomListM'(simpleRandomInit seed) -- Dado n, a quantidade de números aleatórios a serem gerados, devolve -- um transformador de estados que quando executado com a função -- rodaCom devolve um ([Int], State) randomListSizeM' :: Int -> ST RandomState [Int] randomListSizeM' n = replicateM n nextIntM -- Código praticamente idêntico ao randomList2 randomListSizeM :: Int -> Int -> [Int] randomListSizeM seed n = fst $ rodaCom (randomListSizeM' n) (simpleRandomInit seed) -- Estado é dado por (fibx, (penultimo,ultimo)) -- Um passo de transformação nextFiboM :: ST (Integer, Integer) Integer nextFiboM = ST fib where fib (a, b) = (a + b, (b, a + b)) -- >>> rodaCom nextFiboM (2,3) -- (5,(3,5)) -- Vários passos fibosM :: ST (Integer, Integer) [Integer] fibosM = sequence $ repeat nextFiboM fibos :: [Integer] fibos = 0 : 1 : fst (rodaCom fibosM (0, 1)) -- >>> take 100 $ fibos -- [0,1,1,2,3,5,8,13,21,34] main :: IO () main = do putStrLn "Aleatórios" print $ randomList 42 print $ randomListSize 42 3 print $ randomListM 42 print $ randomListSizeM 42 3
Editor Settings
Theme
Key bindings
Full width
Lines