有人可以描述以下类型的构造函数和函数如何工作吗?
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
的代码可以转换为do表示法: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 s a
类型的返回值,而忽略状态。同样,我们可以将
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
的每次调用正确地进行线程化。