按字符频率对字符串进行排序

时间:2015-10-21 21:03:13

标签: list haskell

我刚刚开始使用Haskell,我遇到了以下问题: 我想以特定的方式对String进行排序。所有字符应该彼此相邻,并且它们在结果字符串中的一般位置应该取决于它们在原始字符串中出现的频率。例如:

“aabcbb”应该返回“caabbb”

我的第一个想法就是首先对字符串进行排序,然后将字符分组到列表列表中,比较所述列表的长度并尝试以某种方式对它们进行排序。 但是,我一直试图将字符串转换为字符列表列表。我已经完成了第一次排序:

listify :: String -> [Char]
listify [] = []
listify (x:xs) = [x] ++ listify isInRest ++ listify notInRest
         where
          isInRest = [y | y <-xs, y==x]
          notInRest = [z | z <-xs, z/=x]

这显然会返回一个排序列表,但是按照出现的顺序(在我的上例中将是“aabbbc”)。我真的不知道如何继续前进。

另外,一般来说,我真的不明白为什么我不能以另一种方式处理我的功能的第一个选项。我试过了

listify :: String -> [Char]
listify (x:xs)
        | x == [] = []
        | x == "" = ""

我不同时使用这两个选项,但我不确定如何处理空列表,如果它是这种风格,因为我收到以下错误,我不知道如何处理这些:

enter image description here

任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:4)

标准库中已存在所有这些功能。你能做的最简单的事就是

import Data.Ord
import Data.List

f = concat . sortBy (comparing length) . group . sort

或者,由于length是O(n),这是一种更有效的方法:

import Data.Ord
import Data.List
import Control.Arrow

f = concatMap snd . sortBy (comparing fst) . map (length &&& id) . group . sort

此外,String[Char]相同。

您无法在此处x[]进行比较

listify :: String -> [Char]
listify (x:xs)
        | x == [] = []
        | x == "" = ""

因为x的类型为Char,而不是列表。

答案 1 :(得分:2)

用于此目的的最自然的数据结构可能是优先搜索队列。使用其中一个(您可以从Hackage获得),您可以使用字符作为键和频率计数作为优先级。处理字符串中的所有字符,提升该字符的优先级,最后按优先级顺序将所有字符拉出来。

使用psqueues

import Data.IntPSQ
import Data.Foldable

buildPSQ :: (Foldable t, Ord p, Num p, Enum a) =>
            t a -> IntPSQ p ()
buildPSQ = foldl'
           (\q x -> snd $ PSQ.alter
                          (maybe ((), Just (1,()))
                                 (\(p,_) -> ((), Just (p+1,()))))
                          (fromEnum x) q)
           PSQ.empty

foldrPrio :: Ord p => (Int -> p -> v -> b -> b) -> b -> IntPSQ p v -> b    
foldrPrio k n = maybe n (\(c,p,v,r) -> k c p v (foldrPrio k n r)) . PSQ.minView

rebuild :: Enum a => IntPSQ Int t1 -> [a]
rebuild = foldrPrio (\c p _ r -> replicate p (toEnum c) ++ r) []

sortFreq :: (Foldable f, Enum a) => f a -> [a]
sortFreq = rebuild . buildPSQ