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)]