【发布时间】:2010-09-14 16:40:41
【问题描述】:
是否有标准高阶函数的直接组合来计算列表中的唯一元素?
例如结果
[1, 1, 4, 0, 4, 4]
应该是这样的
[(1,2), (4,3), (0,1)]
【问题讨论】:
-
订单重要吗?如果是这样,顺序是什么?第一次出现的顺序?
是否有标准高阶函数的直接组合来计算列表中的唯一元素?
例如结果
[1, 1, 4, 0, 4, 4]
应该是这样的
[(1,2), (4,3), (0,1)]
【问题讨论】:
使用 Data.Map 和元组部分:
count = Map.fromListWith (+) . map (, 1)
(如果您需要列表,请添加Map.toList。)
【讨论】:
如果顺序不重要,这可行:
map (\xs@(x:_) -> (x, length xs)) . group . sort
group . sort 会给你一个列表列表,其中所有彼此相等的元素都被分组到同一个子列表中(没有排序,只有连续相等的元素会被组合在一起)。然后map 将每个子列表转换为(element, lengthOfSublist)-tuple。
如果要按第一次出现对结果进行排序,可以在排序前使用 zip 为每个元素添加索引,然后在分组后再次按该索引排序,然后删除索引。
【讨论】:
O(n log n)。
insert 一次,并且每次调用 insert 都需要 O(log n) 时间 - 无论该元素是否已经在地图中。
O(log n)。平均而言,它需要 O(log m) 其中 m << n
最简单的方法是对项目进行排序,使用“分组”将它们放入相同元素的子列表中,然后对每个子列表中的项目进行计数。
map (\xs -> (head xs, length xs)) . group . sort
【讨论】:
\xs -> (head xs, length xs) 写成head &&& length。
如果列表只包含整数,你也可以使用
import qualified Data.IntMap as I
countElems1 :: [Int] -> [(Int, Int)]
countElems1 = I.toList . foldr (\k -> I.insertWith (+) k 1) I.empty
(记住要优化编译,否则这将比 group . sort 方法慢 2 倍。使用 -O2 它稍微快 14%。)
您也可以使用multiset packages 之一,它使函数变得如此简单
import qualified Math.Combinatorics.Multiset as S
countElems4 = S.toCounts . S.fromList
但效率较低。
以上所有解决方案都忽略了原来的顺序。
【讨论】:
您所说的只是关于排序数据的run length encoding:免费在线书籍 Real World Haskell 有一个great example of this。您需要在通过 runLengthEncoder 之前对列表进行排序。
【讨论】:
[(1,2),(4,1),(0,1),(4,2)]。