如何在haskell中使用newStdGen或getStdGen而不是mkStdGen

时间:2012-04-19 01:33:30

标签: haskell random

尝试解决99个Haskell问题中的problem 23

我写了这个

rnd_select :: (Eq a) => [a] -> Int -> [a]
rnd_select [] _ = []
rnd_select _ 0 = []
rnd_select ys n = 
   let 
       (rnd_index, gen) = randomR (1, length ys) (mkStdGen 200)
       (x, xs) = removeAt rnd_index ys
   in x : rnd_select xs (n-1)

哪个有效,但我不想使用mkStdGen但是使用

  newStdGen or getStdGen

代替。 我已经看到了问题的解决方案,但是我想知道我应该如何修复这个代码来做到这一点,如果不可能为什么不这样做,因为直觉上它感觉它应该工作,但事实并非如此。

1 个答案:

答案 0 :(得分:9)

请记住,Haskell函数是;在相同输入的情况下,它们必须始终返回相同的结果。您可以让函数返回IO [a],这样可以调用newStdGen,但更好的方法是通过将随机数生成器作为函数的附加参数并返回来保持代码纯净之后的新发电机:

rnd_select :: (Eq a, RandomGen g) => [a] -> Int -> g -> ([a], g)
rnd_select [] _ gen = ([], gen)
rnd_select _ 0  gen = ([], gen)
rnd_select ys n gen = 
   let (rnd_index, gen') = randomR (1, length ys) gen
       (x, xs) = removeAt rnd_index ys
       (xs', gen'') = rnd_select xs (n-1) gen'
   in (x : xs', gen'')

现在你可以使用它,例如getStdRandom :: (StdGen -> (a, StdGen)) -> IO a喜欢这样。

> getStdRandom (rnd_select [1..20] 10)
[12,11,14,4,16,7,1,2,18,15]

然而,手动传递发电机可能有点单调乏味。制作这种整洁器的一种方法是使用MonadRandom包。

rnd_select :: (MonadRandom m, Eq a) => [a] -> Int -> m [a]
rnd_select [] _ = return []
rnd_select _ 0  = return []
rnd_select ys n = do
  rnd_index <- getRandomR (1, length ys)
  let (x, xs) = removeAt rnd_index ys
  xs' <- rnd_select xs (n-1)
  return (x:xs')

由于IOMonadRandom的实例,因此您可以将其直接用作IO操作。

> rnd_select [1..20] 10
[20,18,12,13,5,7,17,9,3,4]
> rnd_select [1..20] 10
[9,18,4,20,6,5,3,15,13,7]

或者您可以使用evalRand在纯monad中运行它,提供您自己的随机数生成器,这样您就可以获得可重复的结果(适合调试/测试)。

> evalRand (rnd_select [1..20] 10) (mkStdGen 200)
[4,16,15,13,8,20,6,14,5,3]
> evalRand (rnd_select [1..20] 10) (mkStdGen 200)
[4,16,15,13,8,20,6,14,5,3]