该System.Random
模块提供了一些基本的原语,但(在我看来)它缺乏适当的一元接口。更准确地说,它提供了一个基于 IO 的单子接口,但缺少一个基于状态的单子接口。不过,后者很容易定义。
无论如何,如果想坚持使用 IO 和标准生成器,可以这样写:
-- (untested code)
rollDie :: Int -> IO Int
rollDie n = randomIO (1,n) -- implicitly uses the global generator
rollManyDice :: Int -> Int -> IO [Int]
rollManyDice howMany n = replicateM howMany (rollDie n)
main :: IO ()
main = do
dice <- rollManyDice 20 6
putStrLn $ "Here's 20 6-sides dice : " ++ show dice
wherereplicateM
执行howMany
一次单子动作,将所有结果收集到一个列表中。monad 在这里IO
,但它可以是任何东西。
这是一种很好很简单的方法,但是IO
上面的类型有点太多了,阻止了我们rollDie
从非 IO 代码中调用。基于-State
的解决方案不会有这个限制
type R a = State StdGen a
rollDie :: Int -> R Int
rollDie n = state $ randomR (1,n) -- uses the generator inside the State monad
rollManyDice :: Int -> Int -> R [Int]
rollManyDice howMany n = replicateM howMany (rollDie n)
main :: IO ()
main = do
g <- getStdGen
let dice = evalState (rollManyDice 20 6) g
putStrLn $ "Here's 20 6-sides dice : " ++ show dice