Мы можем добиться большего успеха, чем nub
(что составляет O(n^2)
), если элементы списка также являются экземплярами Ord
, не требуя тяжелой техники IntMultiSet
, сложив одинаковые элементы вместе с sort
, а затемсгруппировать их в подсписки с group
.Например, group . sort $ [1,2,3,4,4,4]
дает [[1], [2], [3], [4,4,4]]
.
Как только мы упорядочим эти элементы в подсписки, мы можем подсчитать каждый подсписок:
countAll = map (\(x:xs) -> (x, count x (x:xs))) . group . sort
(Обратите внимание, что group
гарантирует, чтоне будет пустых подсписков, поэтому мы знаем, что нам не нужно проверять регистр []
в нашей отображаемой функции, даже если компилятор этого не делает.)
Если нам не нужно использовать count
, мы можем вместо этого использовать
countAll = map (\xs -> (head xs, length xs)) . group . sort
или, с помощью удобного (&&&)
комбинатора из Control.Arrow
,
countAll = map (head &&& length) . group . sort
Поскольку список ассоциаций изэлементы с их количеством - это удобный способ представить сумку , мне нравится называть это
bag = map (head &&& length) . group . sort
bag
равно O(n log n)
, что только на лог-фактор хуже, чемIntMultiSet
версия.