5

99のHaskell問題のうち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

代わりは。私は問題の解決策を見てきましたが、それを行うためにこのコードを修正する方法を理解したいと思います。それが不可能な場合は、直感的には機能するはずなのに機能しないためです。

4

1 に答える 1

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]

ただし、ジェネレータを手動で渡すのはやや面倒です。これをすっきりさせる1つの方法は、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')

IOはのインスタンスなので、これをアクションMonadRandomとして直接使用できます。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これを純粋なモナドで実行して、再現可能な結果を​​取得できるように独自の乱数ジェネレーターを提供することもできます(デバッグ/テストに適しています)。

> 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]
于 2012-04-19T02:23:59.393 に答える