我在我的应用程序中使用System.Random
和Random
类型类来生成随机数。但是我想用randoms :: StdGen -> Int -> ([Float], StdGen)
没有获得新发电机的限制,我可以轻松写
randoms gen n = (take n $ randoms gen) :: [Float]
然而,这让我开始使用相同的随机生成器,这意味着如果我连续两次运行此函数,我将获得相同的列表,除非我去其他地方使用生成器来获取新的生成器。
如何生成无限(或任意长度)随机值列表,同时“刷新”我的随机生成器。
答案 0 :(得分:28)
好吧,让我们来看看你的功能:
random :: StdGen -> (Float, StdGen) -- From System.Random
我们可以将它包装在State
monad中以获得有状态计算:
state :: (s -> (a, s)) -> State s a -- From Control.Monad.Trans.State
random' :: State StdGen Float
random' = state random
现在,我们可以使用replicateM
生成一堆浮点数:
replicateM :: (Monad m) => Int -> m a -> m [a] -- From Control.Monad
randoms' :: Int -> State StdGen [Float]
randoms' n = replicateM n random'
最后,我们打开State
以取回显式生成器传递:
randoms :: Int -> StdGen -> ([Float], StdGen)
randoms n = runState (randoms' n)
如果将所有这些组合成一个函数定义,则得到:
randoms :: Int -> StdGen -> ([Float], StdGen)
randoms n = runState (replicateM n (state random))
换句话说,我们可以将过程描述为:
random
monad State
n
次这就是为什么monads是如此重要的概念。当通过monad界面的镜头观看时,最初看似棘手的事情往往是简单的计算。
答案 1 :(得分:5)
Gabriel的回答是正确的,这几乎是MonadRandom包的实现方式(使用随机生成器参数化的Monad状态)。
它可以节省你每次定义它,它也带有一个Monad变换器,所以你可以将任何其他Monad转换为一个也可以产生随机值的Monad。
您的示例可以轻松实现为:
(runRand $ take n `fmap` getRandoms) :: RandomGen g => g -> ([Int], g)
StdGen恰好是RandomGen的一个实例,所以你可以简单地将其插入并继续!
答案 2 :(得分:3)
不使用State
或split
的替代方案,使用mapAccumL
中的Data.List
(以及来自swap
的{{1}}):
Data.Tuple
虽然我不得不说我没有一个令人信服的论据,为什么这应该以任何方式更好。
答案 3 :(得分:1)
您可以定义一个类型与您想要的类型匹配的函数,尽管更常见。
import System.Random
randoms' :: (RandomGen g, Random a) => g -> Int -> ([a], g)
randoms' g n =
let (g1, g2) = split g
in (take n $ randoms g1, g2)
即使它使用split
split :: g -> (g, g)
split
操作允许获得两个不同的随机数生成器。这在函数式程序中非常有用(例如,将随机数生成器传递给递归调用时),但在split
的统计上强大的实现方面做的工作很少......
它仍然没有做你想要的。 (我在下面的示例中使用Bool
以便于进行视觉比较。)
ghci> g <- getStdGen
ghci> randoms' g 5 :: ([Bool], StdGen)
([False,False,False,True,False],1648254783 2147483398)
ghci> randoms' g 5 :: ([Bool], StdGen)
([False,False,False,True,False],1648254783 2147483398)
请注意,随机数组是相同的。
虽然该功能遇到了拆分发电机的麻烦,但我们会立即丢弃它。相反,通过将g2
线程化为后续调用来使用ghci> let (a1,g2) = randoms' g 5 :: ([Bool], StdGen)
ghci> let (a2,_) = randoms' g2 5 :: ([Bool], StdGen)
ghci> (a1,a2)
([False,False,False,True,False],[True,True,True,False,True]
,如
IO
如果您的代码在myAction :: Int -> IO ([Float],[Float])
myAction n = do
g <- getStdGen
let (f1,g2) = randoms' g n
let (f2,g3) = randoms' g2 n
setStdGen g3
return (f1, f2)
monad中运行,则可以使用setStdGen
替换最后的全局随机数生成器,如
State
线程状态很尴尬且容易出错。如果您有许多重复的样板,请考虑使用ST
或{{1}}。