![](/img/trans.png)
[英]Haskell generate random numbers with a propriety without explicit recursion
[英]Haskell recursion with random numbers and IO
对于99个Haskell问题,特别是第23个问题,我需要
“从列表中提取给定数量的随机选择的元素。
示例(在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
据我所知,这可行,但我关注的是最后两行。 我是新手,我不知道'< - '运算符的相关成本是或者像我一样反复弹跳进出IO。 这是否有效,有没有更好的方法来做到这一点,不涉及弹跳IO,或者没有涉及真正的开销?
您对此有任何见解表示赞赏,因为我最近才开始在Haskell中学习这些更复杂的概念,并且尚未习惯于推理Haskell的IO系统。
解释:为了做到这一点,我决定我应该使用randomR函数从列表中随机选择一个元素(返回给定范围内的随机数),并继续递归,直到我采用n个元素。
我对这个问题做了一些假设,这些假设引导我采用这种方法。 首先,我假设rndSelect只能从列表中选择一个特定元素,其次我假设每个元素应该具有相同的被拾取概率。
PS:这是我关于SO的第一个问题,所以如果我把这个问题格式化,请不要随便告诉我。
您不需要IO,因为randomR不需要它。 但是,您需要做的是通过计算线程化随机数生成器:
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 t, Num a) => [a1] -> a -> t -> ([a1], t)
rndSelect _ 0 g = ([],g)
rndSelect xs n gen =
let (pos, newGen) = randomR (0, length xs - 1) gen
(rest,ng) = rndSelect (removeAt xs pos) (n-1) newGen
in ((xs!!pos):rest, ng)
如果您担心从IO到纯代码的开销,请不要。 相反,您可以尝试使用mwc-random软件包,在这种情况下,它将至少快一个数量级。 此外,如果您有许多元素,您可以使用任何随机访问数据结构而不是列表获得额外的好处。
你可以避免IO为:
rndSelect :: (RandomGen g) => [a] -> Int -> g -> [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
in (xs!!pos):rest
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.