Dav*_*ave 5 io recursion haskell
对于99个Haskell问题,特别是第23个问题,我需要
"从列表中提取给定数量的随机选择的元素.
示例(在lisp中):
(rnd-select '(a b c d e f g h) 3)
(E D A)
Run Code Online (Sandbox Code Playgroud)
"
我实施的是这样的:
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
Run Code Online (Sandbox Code Playgroud)
据我所知,这可行,但我关注的是最后两行.我是新手,我不知道'< - '运算符的相关成本是或者像我一样反复弹跳进出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)
Run Code Online (Sandbox Code Playgroud)
如果您担心从 IO 到纯代码的开销,不用担心。相反,您可以尝试 mwc-random 包,在这种情况下,速度至少会快一个数量级。此外,如果您有许多元素,您可以使用任何随机访问数据结构而不是列表获得额外的好处。
| 归档时间: |
|
| 查看次数: |
692 次 |
| 最近记录: |