繁体   English   中英

随机数和IO的Haskell递归

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM