虽然 JP Moresmau 解决方案当然更可取,但您可能对更透明的解决方案感兴趣,该解决方案可以对 do 表示法和使用 IO 的递归函数有所了解:
import System.Random (randomRIO)
randomList :: Int -> IO([Int])
randomList 0 = return []
randomList n = do
r <- randomRIO (1,6)
rs <- randomList (n-1)
return (r:rs)
你应该注意以下几点:
- 如果
n == 0 函数将使用 return 将空列表包装到 IO 并返回此
- 否则在
do 正文中,它将首先使用randomRIO 生成一个随机数r
- 接下来它将使用
randomList (n-1)递归生成一个n-1元素随机数列表并将其绑定到rs
- 最后它再次使用
return 将r:rs(n 元素列表)包装到IO 中并返回
这是 GHCi 中的一个示例:
λ> randomList 10
[2,4,4,5,2,2,2,5,6,2]
λ> randomList 10
[2,4,4,2,5,2,6,3,4,1]
似乎足够随机
备注/练习:
函数在n 的某些值上存在问题 - 您 能发现吗?如果是这样 - 您可以将函数更改为 total 吗?
玩得开心
如果你仔细观察,你会发现你可以像这样拉出randomRIO (1,6) :: IO Int:
mList :: Int -> IO a -> IO([a])
mList 0 gen = return []
mList n gen = do
r <- gen
rs <- mList(n-1) gen
return (r:rs)
当然你必须像这样使用:
λ> mList 10 $ randomRIO (1,6)
[2,2,2,5,5,1,3,6,6,1]
现在这已经完成(以稍微不同/更好的方式),您可以在Control.Monad 中找到它作为replicateM - 通过此导入,函数简化为:
import System.Random (randomRIO)
import Control.Monad (replicateM)
randomList :: Int -> IO([Int])
randomList n = replicateM n $ randomRIO (1,6)
有趣的事实 internally 这完全按照 JP 的回答来实现 ;)