List 在保持顺序的同时对列表中的元素进行计数
例如,我想知道是否有一种方法可以对列表中的不同元素进行计数,并将计数分组到元组中List 在保持顺序的同时对列表中的元素进行计数,list,haskell,List,Haskell,例如,我想知道是否有一种方法可以对列表中的不同元素进行计数,并将计数分组到元组中 [4,4,4,2,2,2,2,1,1,3] 或 会让步 [(4,3),(2,4),(1,2),(3,1)] 同时保留原始列表的顺序 在评论中提到保留顺序,但从未解决该问题 以下是我迄今为止的尝试: import Data.List (nub) countOccurance :: (Eq a) => a -> [a] -> Int countOccurance x = length . fil
[4,4,4,2,2,2,2,1,1,3]
或
会让步
[(4,3),(2,4),(1,2),(3,1)]
同时保留原始列表的顺序
在评论中提到保留顺序,但从未解决该问题
以下是我迄今为止的尝试:
import Data.List (nub)
countOccurance :: (Eq a) => a -> [a] -> Int
countOccurance x = length . filter (==x)
naiveCounter :: (Eq a) => [a] -> [(a, Int)]
naiveCounter l = map (\x -> (x, countOccurance x l)) $ nub l
但这似乎效率很低。有没有一种方法可以更有效地构造它(例如,只遍历列表一次)
谢谢。你可以用
在ghci中:
> multisetFromList [4,4,4,2,2,2,2,1,1,3]
fromList [(4,3),(2,4),(1,2),(3,1)]
> multisetFromList [2,1,2] -- works with ungrouped lists, too
fromList [(2,2),(1,1)]
你可以用
在ghci中:
> multisetFromList [4,4,4,2,2,2,2,1,1,3]
fromList [(4,3),(2,4),(1,2),(3,1)]
> multisetFromList [2,1,2] -- works with ungrouped lists, too
fromList [(2,2),(1,1)]
正如sepp2k所评论的,您可以在按元素分组后按索引排序。我喜欢用广义的列表理解来表达这一点
{-# LANGUAGE TransformListComp #-}
import GHC.Exts
countOccurrences :: (Ord a) => [a] -> [(a, Int)]
countOccurrences list =
[ (the x, length x)
| (i, x) <- zip [0..] list
, then group by x using groupWith
, then sortWith by minimum i
]
或者生成包含所有部分计数的列表,然后向下过滤到最终计数
import Data.Function
import Data.List
countOccurrences :: (Eq a) => [a] -> [(a, Int)]
countOccurrences = nubBy ((==) `on` fst) . foldr addCount [] where
addCount x counts = (x, maybe 1 succ $ lookup x counts) : counts
尽管两者都不是很有效。正如sepp2k所评论的,您可以在按元素分组后按索引排序。我喜欢用广义的列表理解来表达这一点
{-# LANGUAGE TransformListComp #-}
import GHC.Exts
countOccurrences :: (Ord a) => [a] -> [(a, Int)]
countOccurrences list =
[ (the x, length x)
| (i, x) <- zip [0..] list
, then group by x using groupWith
, then sortWith by minimum i
]
或者生成包含所有部分计数的列表,然后向下过滤到最终计数
import Data.Function
import Data.List
countOccurrences :: (Eq a) => [a] -> [(a, Int)]
countOccurrences = nubBy ((==) `on` fst) . foldr addCount [] where
addCount x counts = (x, maybe 1 succ $ lookup x counts) : counts
虽然两者都不是很有效。另一种选择是两个右折叠:
import Prelude hiding (lookup)
import Data.Map (empty, lookup, delete, insertWith)
count :: (Foldable t, Ord k, Num a) => t k -> [(k, a)]
count xs = foldr go (const []) xs $ foldr (\k -> insertWith (+) k 1) empty xs
where
go x f m = case lookup x m of
Nothing -> f m
Just i -> (x, i): f (delete x m)
那么
另一种选择是两个右折叠:
import Prelude hiding (lookup)
import Data.Map (empty, lookup, delete, insertWith)
count :: (Foldable t, Ord k, Num a) => t k -> [(k, a)]
count xs = foldr go (const []) xs $ foldr (\k -> insertWith (+) k 1) empty xs
where
go x f m = case lookup x m of
Nothing -> f m
Just i -> (x, i): f (delete x m)
那么
根据我对您链接的问题的回答:“如果您想按第一次出现的结果排序,可以在排序之前使用zip向每个元素添加一个索引,然后在分组后,再次按该索引排序,然后删除索引“Hmmm…”。。。。我应该仔细阅读。谢谢:)在我对您链接的问题的回答中:“如果您想按第一次出现的结果排序,可以在排序之前使用zip向每个元素添加索引,然后在分组后,再次按该索引排序,然后删除索引“Hmmm…”。。。。我应该仔细阅读。谢谢:)谢谢,这是一个很好的解决方案,尽管我不想为这个特殊问题使用地图。谢谢,这是一个很好的解决方案,尽管我不想为这个特殊问题使用地图。谢谢,我感谢您的回复。所有这些都很有帮助。谢谢,我很感谢你的回复。所有这些都非常有用。
\> count [4,2,4,4,2,1,2,2,1,3]
[(4,3),(2,4),(1,2),(3,1)]