Problem 23

99 Haskell Problems

Problem 23

Extract a given number of randomly selected elements from a list.

Example:

* (rnd-select '(a b c d e f g h) 3)
(E D A)

Example in Haskell:

Prelude System.Random>rnd_select "abcdefgh" 3 >>= putStrLn
eda

Solution:

Random number seems to be a departure from what I have learn about functional language as it is suppose to be referential transparent. Since I have not deal with random number in Haskell, some reading was needed. In particular, I learn a new notation call do block that helps initialize my random generator g.

rnd_select xs n = do
 g <- newStdGen
 print $ rndS xs n g

rndS [] _ _ = []
rndS xs n g
 | n == 0 = []
 | otherwise = (xs !! r) : rndS xs (n-1) gen
 where (r, gen) = randomR (0, ((length xs) - 1)) g

Using randomR in rndS function, I can pick a number from 0 upto length of the list minus 1 and use that number to pick the element. The most interesting part are the type for rndS and rnd_select:

rnd_select :: (Eq a1, Num a1, Show a) => [a] -> a1 -> IO ()
rndS :: (Eq a1, Num a1, RandomGen t) => [a] -> a1 -> t -> [a]

rndS is expected. However, rnd_select using do block, the output become IO() instead of [a]. A more elegant result from the solution is to use list comprehension:

rnd_select xs n = do
 g <- newStdGen
 return $ take n [ xs !! x | x <- randomRs (0, (length xs) - 1) g]

However, this still using IO[a] instead of [a] as the result. The solution was to use nub in yet another result from the solution section but now if you run the function in consecutive times, the result will be the same.

rnd_select :: [a] -> Int -> [a]
rnd_select x n = map (x!!) is
 where is = take n . nub $ randomRs (0, length x - 1) (mkStdGen 100)

Additional References:

Advertisements