毎回異なる数値を返す純粋な関数を持つことはできません。その出力は、引数だけではなく、以前の呼び出しに依存します。しかし、これまでに生成された数のセットを保持するモナドを作成し、まだ生成されていない数が見つかるまで乱数の生成を再試行することができます。次の例では、MonadRandomパッケージを使用しました。
import Control.Monad
import Control.Monad.Random
import Control.Monad.State
import Data.IntSet (IntSet)
import qualified Data.IntSet as IS
import System.Random (getStdGen)
type RandDistinct g a = StateT IntSet (Rand g) a
evalDistinct :: RandomGen g => RandDistinct g a -> g -> a
evalDistinct k = evalRand (evalStateT k IS.empty)
上記のタイプはStateT
、乱数ジェネレーターを拡張して、これまでに生成された数値のセットを記憶するために使用します。このモナドで計算を評価したいときは、空のセットから始めて、内部の計算を で評価しevalRand
ます。
これで、毎回異なる数値を返す関数を書くことができます:
nextDistinct :: RandomGen g => (Int,Int) -> RandDistinct g Int
nextDistinct range = loop
where
-- Loop until we find a number not in the set
loop = do
set <- get
r <- getRandomR range
if IS.member r set
then loop -- repeat
else put (IS.insert r set) >> return r
そして、それがどのように機能するかをテストします:
main = getStdGen >>= print . evalDistinct (replicateM 50 $ nextDistinct (10, 99))
nextDistinct
単純な戦略を使用することに注意してください-セットに既に存在する場合は、新しい番号の生成を再試行してください。衝突の数が少ない限り、これは問題なく機能します。