仅使用比较运算符即可计算列表中每个元素的频率

时间:2019-04-19 10:00:52

标签: haskell

我是Haskell的新手,我必须计算列表中元素的频率并将其返回到键值对列表中,其中key是列表中的元素,而value是它在其中发生的次数。名单。例如:

对于输入[0,2,2,0,2,5,0,2],输出应为[(0,3),(2,4),(5,1)]

这里的难点是我们必须做到这一点,除了比较之外,不使用任何预定义的功能

import Data.Map (fromListWith, toList)

frequency :: (Ord a) => [a] -> [(a, Int)]
frequency xs = toList (fromListWith (+) [(x, 1) | x <- xs])

这有效,但是toList和fromListWith分类为预定义函数。不用使用任何Data.Map函数就可以做到这一点吗?

1 个答案:

答案 0 :(得分:2)

尝试这个

freq:: (Ord a) => [a] -> [(a, Int)]
freq []      = []
freq (x: xs) = ins x (freq xs)
   where 
      ins :: (Ord a) => a -> [(a, Int)] -> [(a, Int)]
      ins x ((fk, fv): fs) | x == fk = (fk, fv + 1): fs 
      ins x ((fk, fv): fs) | x <  fk = (x, 1): (fk, fv): fs       
      ins x ((fk, fv): fs)           = (fk, fv): ins x fs             
      ins x []                       = (x, 1): []             

ins函数在地图中插入单个元素。 它是递归地穿过地图。

如果当前元素等于键,则将其值递增。

ins x ((fk, fv): fs) | x == fk = (fk, fv + 1): fs 

如果当前元素小于键,则将其插入地图。

ins x ((fk, fv): fs) | x <  fk = (x, 1): (fk, fv): fs

否则(如果元素更大),它将尝试在地图的尾部插入元素。

ins x ((fk, fv): fs)           = (fk, fv): ins x fs

最后一部分是地图为空时的简单处理情况。