Stuart Nelson
Stuart Nelson

Reputation: 4202

Sample arbitrary amount of numbers from Haskell list

I'm starting to learn Haskell from a Ruby background. I'm looking to be able to get an arbitrary number of items from a list:

sample [1,2,3,4,5,6,7,8,9,10]
=> 7
sample 3 [1,2,3,4,5,6,7,8,9,10]
=> [4,2,9]

This is available in Ruby and I'm hoping to get the same functionality. I haven't been able to find it after some googling, so I figured I would ask here. Is this available or is it a function I would have to implement myself? Thanks!

Upvotes: 2

Views: 752

Answers (3)

Levi Pearson
Levi Pearson

Reputation: 4984

Based on the code at http://ruby-doc.org/core-2.0/Array.html for sample, which chooses n random indices into the array, I came up with the following:

import System.Random
import Data.List
import Control.Applicative

sample1 xs = do
  let l = length xs - 1
  idx <- randomRIO (0, l)
  return $ xs !! idx

sample 0 xs = return []
sample n xs = do
  let l = min n (length xs)
  val <- sample1 xs
  (:) <$> (pure val) <*> (sample (l-1) (delete val xs))

Alternatively, you could use Control.Monad instead of Control.Applicative and liftM2 (:) (return val) (sample (ct-1) (delete val xs))

Using delete does incur an Eq constraint on the type of the list elements, though, so you would have to split/merge at the index if you needed to avoid that.

Upvotes: 1

Nicolas
Nicolas

Reputation: 24759

You can use the random.shuffle package, which comes with a shuffle' function. But you need a Random generator.

You can also look for further explanation on the haskell wiki: http://www.haskell.org/haskellwiki/Random_shuffle

Once your list is shuffled, you can take n elements of it.

Upvotes: 2

DiegoNolan
DiegoNolan

Reputation: 3766

Code:

import System.Random

sample :: Int -> [a] -> IO [a]
sample count lst = go count lst []
   where go 0 _ acc = return acc
         go count xs acc = do
            rnd <- randomIO :: IO Int
            let ind = rnd `rem` (length xs)
                (beg, (r:rs)) = splitAt ind xs
            go (count-1) (beg ++ rs) (r : acc)

Since random number generation is impure you need to be in the IO monad, although you could generate a random seed and then pas that to the function. This code gets a random int, makes sure it is inside the bounds of the list. Then splits the list and returns that number thus removing it from the list and then recursing until no more numbers are needed.

Upvotes: 0

Related Questions