2012-04-08 106 views
2

假设我有一个像计数频率值

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 

这工作,但我看到至少有两个问题:

  1. 它使用length功能。
  2. 它要求调用者在第一个参数中指定所有可能的值。

有没有办法改善这种情况?

+1

是类型声明错误有键值对?为什么'countEnum'需要两个输入? – is7s 2012-04-08 17:50:12

+0

@ is7s:第一个参数是一个包含所有可能值的列表(主要是为了找出有多少个值)。 – Philipp 2012-04-08 18:21:42

回答

5

通常比排序列表有点快正在使用Map,

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

,你可以得到

  • 频率仅为每Map.elems $ enumFreq list
  • 的对(value,frequency)[(toEnum i, f) | (i,f) <- Map.assocs $ enumFreq list]

如果你的类型本身就是Ord,你可以跳过fromEnumtoEnum

如果你有IxBounded实例和类型没有太多的元素,

import Data.Array.Unboxed 

enumFreq :: (Ix a, Bounded a) => [a] -> UArray a Word 
enumFreq = accumArray (+) 0 (minBound,maxBound) . (`zip` repeat 1) 

具有更好的渐进性,使用较少的内存和更快已经是相当短名单。 (但是,这取决于类型的元素存在于名单的比例很高。)

+0

谢谢,这正是我需要的。同时我发现了一个基于'Map'的类似解决方案,但是你的方法更加简洁。 – Philipp 2012-04-08 19:31:13

4

也许这样?

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] 

如果你可以定义一个Bounded实例T则有可能数为零事件:

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

> countEnum' [C, C, A, C, A, C] 
[2,0,4] 
+0

看起来非常好,但如果不是所有可能的元素实际上都出现在输入列表中(结果列表中的相应元素被忽略,它应该为零),它就不起作用。 – Philipp 2012-04-08 18:28:02

+0

@Philipp我不认为这是可能的,如果没有'Bounded'实例或显式参数,就像在你的初始例子中那样。 – 2012-04-08 18:38:49

+1

'enumFromTo minBound maxBound'可以写成'[minBound .. maxBound]' – newacct 2012-04-08 20:07:58

2

如果你有Ord,您可以通过使用

import Control.List 
import Control.Arrow 

map (head &&& length) $ group $ sort elems