2012-04-08 108 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