生成随机值列表,并获得新的生成器

Sam*_*ern 13 random haskell

我在我的应用程序中使用System.RandomRandom类型类生成随机数.但是我想生成一个任意长度的随机浮点列表,其函数类似于randoms :: StdGen -> Int -> ([Float], StdGen)

没有获得新发电机的限制,我可以很容易地写 randoms gen n = (take n $ randoms gen) :: [Float]

然而,这让我开始使用相同的随机生成器,这意味着如果我连续两次运行此函数,我将获得相同的列表,除非我去其他地方使用生成器获取新的生成器.

如何生成无限(或任意长度)随机值列表,同时"刷新"我的随机生成器.

Gab*_*lez 28

好吧,让我们来看看你的功能:

random :: StdGen -> (Float, StdGen)  -- From System.Random
Run Code Online (Sandbox Code Playgroud)

我们可以将它包装在Statemonad中以获得有状态的计算:

state :: (s -> (a, s)) -> State s a  -- From Control.Monad.Trans.State

random' :: State StdGen Float
random' = state random
Run Code Online (Sandbox Code Playgroud)

现在,我们可以使用replicateM以下方法生成一堆浮点数:

replicateM :: (Monad m) => Int -> m a -> m [a]  -- From Control.Monad

randoms' :: Int -> State StdGen [Float]
randoms' n = replicateM n random'
Run Code Online (Sandbox Code Playgroud)

最后,我们打开它State来取回显式生成器传递:

randoms :: Int -> StdGen -> ([Float], StdGen)
randoms n = runState (randoms' n)
Run Code Online (Sandbox Code Playgroud)

如果将所有这些组合成一个函数定义,您将获得:

randoms :: Int -> StdGen -> ([Float], StdGen)
randoms n = runState (replicateM n (state random))
Run Code Online (Sandbox Code Playgroud)

换句话说,我们可以将过程描述为:

  • 包裹randomStatemonad中
  • 复制它的n次数
  • 打开它

这就是为什么monads是如此重要的概念.当通过monad接口的镜头观看时,最初看起来很棘手的事情往往是简单的计算.


小智 5

Gabriel的答案是正确的,这几乎是MonadRandom包的实现方式(使用随机生成器参数化的Monad状态).

它可以节省您每次定义它,它也带有Monad变换器,因此您可以将任何其他Monad转换为也可以生成随机值的Monad.

您的示例可以轻松实现为:

(runRand $ take n `fmap` getRandoms) :: RandomGen g => g -> ([Int], g)
Run Code Online (Sandbox Code Playgroud)

StdGen恰好是RandomGen的一个实例,所以你只需将其插入即可!