具有随机数和IO的Haskell递归

具有随机数和IO的Haskell递归,haskell,recursion,io,Haskell,Recursion,Io,对于99个Haskell问题,特别是其中一个,我需要 “从列表中提取给定数量的随机选择元素 示例(在lisp中): " 我是这样实施的: import System.Random import Control.Monad removeAt :: [a] -> Int -> [a] removeAt (x:xs) i | i > 0 = x : removeAt xs (i-1) | otherwise = xs rndSelect :: (RandomGe

对于99个Haskell问题,特别是其中一个,我需要

“从列表中提取给定数量的随机选择元素

示例(在lisp中):

"

我是这样实施的:

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
导入系统。随机
进口管制
移除::[a]->Int->[a]
移除(x:xs)i
|i>0=x:移除x(i-1)
|否则=xs
rndSelect::(RandomGen g)=>[a]->Int->g->IO[a]
rndSelect_u0_u0=return[]
rndSelect xs n gen=do
let(pos,newGen)=randomR(0,长度xs-1)gen

rest您不需要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随机软件包,在这种情况下,它至少要快一个数量级。此外,如果您有许多元素,使用任意随机访问数据结构而不是列表可以获得额外的好处。

您可以避免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

您真的需要将结果封装在IO monad中吗?为什么不能是:rndSelect::(RandomGen g)=>[a]->Int->g->[a]。。。。如果需要,这个函数的用户可以使用return函数将结果列表包装在IO中。我使用IO是因为这是(我知道的)获取随机数的唯一方法。我希望从列表中随机选择/删除一个元素,然后返回列表,这样我可以再次(n-1)次这样做。由于Haskell的纯洁性,我不认为我可以在IO单子之外完成这项工作,但可能会编写一个不使用IO的助手函数。对,我明白了。我感到困惑,并认为我需要IO,因为我在LYAH阅读关于getStdGen的文章,而我所需要的只是开始时的种子随机发生器。>如果您担心从IO到纯代码的开销,请不要担心。谢谢优化这个程序并不重要,重要的是找出IO是否有相关的成本,所以很高兴知道。谢谢,我刚刚得到了aleator的答案,但你是对的,我错误地认为我需要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