具有随机数和IO的Haskell递归
对于99个Haskell问题,特别是其中一个,我需要 “从列表中提取给定数量的随机选择元素 示例(在lisp中): " 我是这样实施的:具有随机数和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
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