より一般的ではありますが、必要なタイプと一致するタイプの関数を定義できます。
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
操作により、2つの異なる乱数ジェネレーターを取得できます。これは関数型プログラムでは非常に便利ですが(たとえば、乱数ジェネレーターを再帰呼び出しに渡す場合)、統計的に堅牢なsplit
…</p>
の実装についてはほとんど作業が行われていません。
それでもあなたが望むことをしません。(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
モナドで実行されている場合は、次setStdGen
のように、最後にグローバル乱数ジェネレーターを置き換えるために使用できます。
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)
スレッド化の状態は扱いにくく、エラーが発生しやすくなります。ボイラープレートを繰り返し使用するState
か、使用することを検討してください。ST