In addition to the other answers: If you want to have fast, imperative solution in Haskell, the way to go would be the [ST
monad]. You can use STArray
or even STUArray
(unboxed, which means it's strict and memory efficient - no thunks):
import Control.Monad (forM_)
import Control.Monad.ST
import Data.Array.ST
import Data.Array.Unboxed (UArray)
count :: (Int, Int) -> [Int] -> UArray Int Int
count b@(l, h) xs = runSTUArray $ do
arr <- newArray b 0
forM_ xs $ \x -> readArray arr x >>= writeArray arr x . (+ 1)
return arr
This returns a pure result, but works somewhat more efficiently than Map
(which is otherwise the way to go in Haskell), at the expense of being more imperative.
On the other side of the spectrum, a way how to look at the problem is how to construct the simplest histogram - from a single value, and then how to combine histograms together. A one-element histogram (represented as Map a Int
) is singleton x 1
and they can be combined together by combining maps with unionWith
(here generalized to arbitrary values that can be ordered):
import qualified Data.Map as M
count :: (Ord a) => [a] -> M.Map a Int
count = M.unionsWith (+) . map (\x -> M.singleton x 1)
If you want to go even further in this direction, you can realize that distributions are monoids:
import qualified Data.Map as M
newtype Distribution a = Distribution { getDistribution :: M.Map a Int }
deriving (Show, Read, Eq, Ord)
instance (Ord a) => Monoid (Distribution a) where
mempty = Distribution M.empty
mappend (Distribution m1) (Distribution m2) = Distribution $ M.unionWith (+) m1 m2
singleton :: (Ord a) => a -> Distribution a
singleton x = Distribution $ M.singleton x 1
count :: (Ord a) => [a] -> Distribution a
count = foldMap singleton
It's somewhat more verbose, but separates the general properties of distributions (the Monoid
instance) from the final computation, which then becomes trivial. Another advantage is that the newtype
allows to hide the internal representation, which can be potentially replaced by something else, while keeping the interface intact.