Suppose I have an enum like
data T = A | B | C deriving (Enum)
and a list of enum values as input:
[B, C, C, A, C, A, C]
What I’m looking for is a function that, given this input, returns how often each element occurs in the input. A simple form for the output would be a list of the frequencies ([2, 1, 4] in this case), but this is not a requirement. My current approach looks like this:
countEnum :: Enum a => [a] -> [a] -> [Word]
countEnum elems =
let f x = map (fromIntegral . fromEnum . (fromEnum x ==)) [0 .. length elems - 1]
in foldr (zipWith (+)) (replicate (length elems) 0) . map f
This works, but I see at least two issues:
- It uses the
lengthfunction. - It requires that the caller specifies all possible values in the first argument.
Is there a way to improve this?
Usually a bit faster than sorting a list is using a
Map,and you can get
Map.elems $ enumFreq list(value,frequency)per[(toEnum i, f) | (i,f) <- Map.assocs $ enumFreq list]If your type is itself in
Ord, you can skip thefromEnumandtoEnum.If you have
IxandBoundedinstances and the type doesn’t have too many elements,has better asymptotic behaviour, uses less memory and is faster already for fairly short lists. (But that depends on a high proportion of the type’s elements being present in the list.)