【发布时间】:2011-05-10 05:13:53
【问题描述】:
对于 99 个 Haskell 问题,特别是 23rd 一个,我需要
"从列表中随机抽取给定数量的元素。
示例(在 lisp 中):
(rnd-select '(a b c d e f g h) 3)
(E D A)
"
我是这样实现的:
import System.Random
import Control.Monad
removeAt :: [a] -> Int -> [a]
removeAt (x:xs) i
| i > 0 = x : removeAt xs (i-1)
| otherwise = xs
rndSelect :: (RandomGen g) => [a] -> Int -> g -> IO [a]
rndSelect _ 0 _ = return []
rndSelect xs n gen = do
let (pos, newGen) = randomR (0, length xs - 1) gen
rest <- rndSelect (removeAt xs pos) (n-1) newGen
return $ (xs!!pos):rest
-- for an explanation of what this is doing see EXPLANATION below
据我所知,这是可行的,但我关心的是最后两行。我是新手,我不知道 '
感谢您的任何见解,因为我最近才开始在 Haskell 中学习这些更复杂的概念,还没有习惯于推理 Haskell 的 IO 系统。
解释:为了做到这一点,我决定我应该使用 randomR 函数从列表中随机选择一个元素(返回给定范围内的随机数),并继续递归执行此操作,直到我采取了 n元素。
我对导致我采用这种方法的问题做了几个假设。首先,我假设 rndSelect 只能从列表中选择一个特定元素一次,其次我假设每个元素应该具有相同的被选中概率。
PS:这是我关于 SO 的第一个问题,所以如果我的问题格式不正确,请随时告诉我。
【问题讨论】:
-
你真的需要将结果包装在 IO monad 中吗?为什么不能是:rndSelect :: (RandomGen g) => [a] -> Int -> g -> [a] ...。如果需要,此函数的用户可以使用返回函数将结果列表包装在 IO 中。
-
我使用 IO 是因为这是(我知道的)获取随机数的唯一方法。我希望从列表中随机选择/删除一个元素,然后返回列表,以便我可以再次执行此操作 (n-1) 次。由于 Haskell 的纯洁性,我认为我不能在 IO monad 之外执行此操作,但可以编写一个不使用 IO 的辅助函数。