可能有人形容以下类型的构造和功能是如何工作的?
type Rand a = State StdGen a
getRandom :: (Random a) => Rand a
getRandom = get >>= (\r -> let (a,g) = random r in (put g) >> (return a))
runRand :: Int -> Rand a -> a
runRand n r = evalState r $ mkStdGen n
runRandIO :: Rand a -> IO a
runRandIO r = randomIO >>= (\rnd -> return $ runRand rnd r)
getRandoms :: (Random a) => Int -> Rand [a]
getRandoms n = mapM (\_ -> getRandom) [1..n]
让我们从头开始:
type Rand a = State StdGen a
这行告诉你, Rand a
是对一种类型的代名词State
类型,其状态由给定StdGen
,其最终的价值型的a
。 这将被用来存储每个请求之间的随机数生成器的状态为一随机数。
对于代码getRandom
可以转换成做记号:
getRandom :: (Random a) => Rand a
getRandom = do
r <- get -- get the current state of the generator
let (a,g) = random r in do -- call the function random :: StdGen -> (a, StdGen)
put g -- store the new state of the generator
return a -- return the random number that was generated
所述runRand
函数接受一个初始种子n
和值r
型的Rand a
(其中,记住,仅仅是一个同义词State StdGen a
)。 它创建一个新的发电机mkStdGen n
和饲料它evalState r
功能evalState
只计算一个返回值State sa
型,忽视的状态。
同样,我们可以将runRandIO
到do
记号:
runRandIO :: Rand a -> IO a
runRandIO r = do
rnd <- randomIO -- generate a new random number using randomIO
return (runRand rnd r) -- use that number as the initial seed for runRand
最后, getRandoms
花费数n
表示要生成的随机值的数量。 它建立一个列表[1..n]
和适用getRandom
到列表中。 需要注意的是在实际值[1..n]
未使用(你可以告诉,因为lambda函数开头\_ -> ...
)。 该名单只是那里有正确数量的元素的东西。 由于getRandom
返回一个monadic值,我们使用mapM
映射在所述列表中,这会导致状态(即StdGen
)要通过每个调用的正确螺纹getRandom
。
基本想法很简单 - 创造伪随机数,你需要维护函数调用之间的一些状态。 因此,类型Rand a
的定义是指“ a
有需要的随机性国家一起”。
国家将使用存储State
单子 。 这提供了两个主要actions-- get
和put
,这不,他们的声音究竟是什么样子。 所以getRandom
刚刚抬头的当前状态,然后调用random
函数。 该函数返回两个值:随机值和新的状态。 然后你只需put
新的状态和包装所产生的价值。
runRand
让你解开给予种子“随机”值。 evalState
让你执行一个有状态计算(也就是类型的值State sa
或者,在这种情况下, Rand a
)给定的初始状态,然后就放弃最终状态,只给你结果。 因此,这允许您运行Rand a
给定的种子,只返回结果值。 该值可以只输入了a
,而不是Rand a
因为它总是给你相同的结果为相同的种子。
runRandomIO
只是做同样的事情,除了基于种子关在IO一些全局状态得到。
getRandoms
只是让你列表Rand a
致电值getRandom
为的每一个元素[1..n]
列表(忽略实际数量)。