Haskell 枚举值的计数频率

Haskell 枚举值的计数频率,haskell,enums,Haskell,Enums,假设我有一个像 data T = A | B | C deriving (Enum) 以及作为输入的枚举值列表: [B, C, C, A, C, A, C] 我要找的是一个函数,给定这个输入,它返回每个元素在输入中出现的频率。输出的简单形式是频率列表(在本例中为[2,1,4]),但这不是要求。我目前的做法如下: countEnum :: Enum a => [a] -> [a] -> [Word] countEnum elems = let f x = map (fr

假设我有一个像

data T = A | B | C deriving (Enum)
以及作为输入的枚举值列表:

[B, C, C, A, C, A, C]
我要找的是一个函数,给定这个输入,它返回每个元素在输入中出现的频率。输出的简单形式是频率列表(在本例中为
[2,1,4]
),但这不是要求。我目前的做法如下:

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
这是可行的,但我至少看到两个问题:

  • 它使用
    length
    功能
  • 它要求调用方在第一个参数中指定所有可能的值

  • 有什么办法可以改进吗?

    也许是这样的

    import Control.Arrow ((&&&))
    import Data.Function (on)
    import Data.List (groupBy, sortBy)
    
    data T = A | B | C deriving Enum
    
    countEnum :: Enum a => [a] -> [Int]
    countEnum = map length . groupBy ((==) `on` snd) . sortBy (compare `on` snd) . map (id &&& fromEnum)
    
    例如:

    > countEnum [B, C, C, A, C, A, C]
    [2,1,4]
    
    如果您可以为
    T
    定义一个
    Bounded
    实例,则有可能计算零次出现:

    countEnum' :: (Bounded a, Enum a) => [a] -> [Int]
    countEnum' = map pred . countEnum . (++ enumFromTo minBound maxBound)
    
    > countEnum' [C, C, A, C, A, C]
    [2,0,4]
    

    通常使用
    映射比排序列表快一点

    enumFreq :: Enum a => [a] -> Map Int Word
    enumFreq = foldl' (\mp e -> Map.insertWith' (+) (fromEnum e) 1 mp) Map.empty
    
    你可以得到

    • 仅根据
      Map.elems$enumFreq列表列出频率

    • 成对的
      (值、频率)
      [(toEnum i,f)|(i,f)如果您有
      Ord
      ,您可以使用

      import Control.List
      import Control.Arrow
      
      map (head &&& length) $ group $ sort elems
      

      类型声明是否错误?为什么
      countEnum
      需要两个输入?@is7s:第一个参数是一个包含所有可能值的列表(主要是为了找出有多少个值)。这看起来很不错,但是如果不是所有可能的元素都出现在输入列表中,它就不起作用了(结果列表中的对应元素被省略,它应该是零)。@Philipp我认为如果没有
      Bounded
      实例或初始示例中的显式参数,这是不可能的。
      enumFromTo minBound maxBound
      可以写成
      [minBound..maxBound]
      谢谢,这正是我所需要的。同时,我发现了一个基于
      Map
      的类似解决方案,但你的更简洁。
      import Control.List
      import Control.Arrow
      
      map (head &&& length) $ group $ sort elems