実際には、状態モナドのバリアントを使用して、乱数ジェネレーターを舞台裏で渡します。Rand
タイプインはこれにControl.Monad.Random
役立ちます。API は少し混乱しますが、それは機能する必要があるというよりも、使用するランダム ジェネレーターの種類が多態的であるためです。ただし、この余分な足場は便利です。既存のコードをさまざまなランダム ジェネレーターで簡単に再利用できるため、さまざまなアルゴリズムをテストできるだけでなく、ジェネレーターが決定論的 (テストに適している) か、外部データをシードする ( IO
)。
動作中の簡単な例を次に示しRand
ます。RandomGen g =>
型シグネチャのは、任意の型の乱数発生器を使用できることを示しています。明示的に として注釈n
を付ける必要があります。そうしないと、生成して文字列に変換できる数値型でなければならないInt
ことを GHC しか認識していないためです。Double
randomReplace :: RandomGen g => String -> Rand g String
randomReplace = foldM go ""
where go str '#' = do
n :: Int <- getRandomR (0, 10)
return (str ++ show n)
go str chr = return $ str ++ [chr]
これを実行するには、どこかから乱数発生器を取得して に渡す必要がありevalRand
ます。これを行う最も簡単な方法は、で実行できるグローバル システム ジェネレーターを取得することですIO
。
main :: IO ()
main = do gen <- getStdGen
print $ evalRand (randomReplace "ab#c#") gen
これは非常に一般的なパターンであり、ライブラリevalRandIO
がそれを行う関数を提供します:
main :: IO ()
main = do res <- evalRandIO $ randomReplace "ab#c#"
print res
最終的に、コードは乱数発生器を使用してそれを渡すことについてもう少し明示的ですが、それでもかなり簡単に理解できます。より複雑なコードについてはRandT
、 を使用することもできます。これにより、他のモナド ( などIO
) を拡張してランダムな値を生成することができ、すべての配管とセットアップをコードの一部に任せることができます。