您没有说是否要自己编写它,或者是否可以从一些标准函数组合它。
import Data.List
g s = map (\x -> ([head x], length x)) . group . sort $ s
-- g = map (head &&& length) . group . sort -- without the [...]
是标准的快速n脏编码方式。
好的,所以您最初的想法是编写无点样式(某些曲调在我脑海中播放...):
frequencyOfElt :: (Eq a) => [a] -> [(a,Int)]
frequencyOfElt xs = countElt (unique xs) xs -- change the result type
where
unique [] = []
unique (x:xs) = x : unique (filter (/= x) xs)
countElt ref target = -- Code it Point-Free Style (your original idea)
zip
ref $ -- your original type would need (map (:[]) ref) here
map length $
zipWith ($) -- ((filter . (==)) c) === (filter (== c))
(zipWith ($) (repeat (filter . (==))) ref)
(repeat target)
我已将此处的类型更改为更合理的[a] -> [(a,Int)]
btw。注意
zipWith ($) fs (repeat z) === map ($ z) fs
zipWith ($) (repeat f) zs === map (f $) zs === map f zs
因此代码简化为
countElt ref target =
zip
ref $
map length $
map ($ target)
(zipWith ($) (repeat (filter . (==))) ref)
接着
countElt ref target =
zip
ref $
map length $
map ($ target) $
map (filter . (==)) ref
但是map f $ map g xs === map (f.g) xs
,所以
countElt ref target =
zip
ref $
map (length . ($ target) . filter . (==)) ref -- (1)
用列表理解写的更清晰(根据我的口味),
countElt ref target =
[ (c, (length . ($ target) . filter . (==)) c) | c <- ref]
== [ (c, length ( ($ target) ( filter (== c)))) | c <- ref]
== [ (c, length $ filter (== c) target) | c <- ref]
这给了我们一个想法,将 (1) 进一步重写为
countElt ref target =
zip <*> map (length . (`filter` target) . (==)) $ ref
但是这种对无点代码的痴迷在这里变得毫无意义。
因此,回到可读的列表推导,使用nub
等效于您的标准函数unique
,您的想法变成
import Data.List
frequencyOfElt xs = [ (c, length $ filter (== c) xs) | c <- nub xs]
这个算法实际上是二次的(~ n^2
),所以它比上面的第一个版本更糟糕,sort
即以线性(~ n log(n)
)为主导。
这段代码虽然可以通过等效转换原则进一步操作:
= [ (c, length . filter (== c) $ sort xs) | c <- nub xs]
...因为在列表中搜索与在列表中搜索相同,已排序。在这里做更多的工作——它会得到回报吗?
= [ (c, length . filter (== c) $ sort xs) | (c:_) <- group $ sort xs]
... 正确的?但是现在,group
已经将它们分组了(==)
,所以不需要filter
调用重复已经完成的工作group
:
= [ (c, length . get c . group $ sort xs) | (c:_) <- group $ sort xs]
where get c gs = fromJust . find ((== c).head) $ gs
= [ (c, length g) | g@(c:_) <- group $ sort xs]
= [ (head g, length g) | g <- group (sort xs)]
= (map (head &&& length) . group . sort) xs
不是吗?在这里,与本文开头的线性算法相同,实际上是通过分解隐藏的常见计算从您的代码中派生出来的,使它们可用于重用和代码简化。