choose-0.1.0.0: Data/Random/Choose/IO.hs
{- |
'IO'-specific functionality around the random selection algorithm described in
"Data.Random.Choose".
-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.Random.Choose.IO ( getSelectionsIO ) where
--------------------------------------------------------------------------------
import qualified Data.Random.Choose.Tree as Tree
import Data.Random.Choose.Tree (Tree)
import Control.Monad.Random (evalRandIO)
import qualified Data.Foldable as Foldable
import Data.Function ((&))
import Data.List (sort)
--------------------------------------------------------------------------------
getSelectionsIO :: forall a. (Ord a)
=> IO (Maybe a) -- ^ Producer of items to choose from; produces 'Nothing'
-- when there are no more items)
-> Int -- ^ /n/: Number of items to choose
-> IO [a] -- ^ /n/ of the items (or all of the items if fewer than
-- /n/ are produced)
-- ^ Selects /n/ items uniformly at random from an input stream.
--------------------------------------------------------------------------------
getSelectionsIO getItem limit = f 0 Tree.empty
where
-- We store each line of text along with its index (i) so that when we're
-- done, we can sort by the index, thus outputting the selected items in
-- the same order in which they were read.
f :: Int -> Tree (Int, a) -> IO [a]
f i tree = do
lineMaybe <- getItem
case lineMaybe of
-- We read a line; insert it into the tree and recurse.
Just line -> do
tree' <- evalRandIO ( tree
& Tree.insert (i, line)
& Tree.applyLimit limit
)
f (i + 1) tree'
-- We've reached the end of the input. Convert the tree to a list,
-- sort it, and strip out the indices to return just the text.
Nothing -> return $ snd <$> sort (Foldable.toList tree)