全体を自分で書きたいのか、それともいくつかの標準的な関数で構成してもいいのか、ということはありませんでした。
import Data.List
g s = map (\x -> ([head x], length x)) . group . sort $ s
-- g = map (head &&& length) . group . sort -- without the [...]
それをコーディングするための標準的なquick-n-dirty方法です。
さて、あなたの最初のアイデアは、ポイントフリースタイルでコーディングすることでした(私の頭の中で特定の曲が演奏されています...):
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)]
。ご了承ください
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]
このアルゴリズムは実際には2次式( )である~ 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
ではない?そして、これが、この投稿の冒頭からの同じ線形アルゴリズムであり、実際には、隠された一般的な計算を除外してコードから派生し、再利用とコードの簡素化に利用できるようにしています。