在Haskell中建立一个列表

时间:2015-09-11 14:08:14

标签: haskell

我想要做的是创建一个随机整数列表,没有重复。作为第一步,我有一个函数,它列出了n个随机样本。如何以更加Haskell惯用的方式写这个,其中不需要传入空列表来启动列表?我确信我遗漏了一些基本和基本的东西。

-- make a list of random integers.
-- takes a size, and an empty list.
-- returns a list of that length of random numbers.
f :: Int -> [Int] -> IO [Int]
f l xs | length xs >= l = return (xs)
f l xs = do
  r <- randomRIO (1, 40) :: IO Int
  f l $ r : x

用法:

*Main> f 6 []
[10,27,33,35,31,28]

最终,此函数将进行过滤以检查重复插入,但这是一个单独的问题。虽然这可能看起来像是家庭作业,但事实并非如此,而是我自己试图掌握State monad用于随机数生成的一部分,并发现我被困在更早的地方。

2 个答案:

答案 0 :(得分:3)

好吧,你可以对递归调用的输出进行操作:

f :: Int -> IO [Int]
f 0 = return []
f n = do
    r <- randomRIO (1, 40)
    xs <- f (n-1)
    return $ r : xs

但请注意,您对结果执行的操作很快很重要。在这种情况下,r : xs是恒定时间。但是,如果用(例如)替换最后一行:

    return $ xs ++ [r]

这会将函数的复杂性从线性变为二次,因为每个++调用必须先扫描所有先前生成的数字序列,然后再附加新数据。

但是你可以这样做:

f n = sequence $ replicate n (randomRIO (1, 40))

replicate创建一个由[IO Int]个操作构成的n长度randomRIO列表,sequence获取[IO a]并将其转换为IO [a] {1}}按顺序执行所有操作并收集结果。

更简单的是,您可以使用已经是您想要的功能的replicateM

import Control.Monad(replicateM)

f n = replicateM n (randomRIO (1, 40))

或以无点样式:

f :: Int -> IO [Int]
f = flip replicateM $ randomRIO (1, 40)

答案 1 :(得分:1)

这使用Set来跟踪已生成的数字:

import System.Random
import qualified Data.Set as Set

generateUniqueRandoms :: (Int, Int) -> Int -> IO [Int]
generateUniqueRandoms range@(low, high) n =
  let maxN = min (high - low) n
  in
     go maxN Set.empty

  where
     go 0 _ = return []
     go n s = do
        r <- getUniqueRandom s
        xs <- go (n-1) (Set.insert r s)
        return $ r : xs

     getUniqueRandom s = do
         r <- randomRIO range
         if (Set.member r s) then getUniqueRandom s
         else return r

以下是一些示例输出:

Main> generateUniqueRandoms (1, 40) 23
[29,22,2,17,5,8,24,27,10,16,6,3,14,37,25,34,30,28,7,31,15,20,36]

Main> generateUniqueRandoms (1, 40) 1000
[33,35,24,16,13,1,26,7,14,11,15,2,4,30,28,6,32,25,38,22,17,12,20,5,18,40,36,39,27,9,37,31,21,29,8,34,10,23,3]

Main> generateUniqueRandoms (1, 40) 0
[]

然而,值得注意的是,如果n接近范围的宽度,则可以更有效地对范围内所有数字的列表进行混洗并获取第一个n 1}}那个。