字符串到多集 haskell

string to multi set haskell

将字符串转换为多重集,如下例:

"bacaba" --> [(b,2),(a,3),(c,1)]

type MSet a = [(a,Int)]

convert :: Eq a => [a] -> MSet

我的代码有什么问题,更好的方法是什么?泰

convert :: Eq a => [a] -> MSet a
convert [] = []
convert (x:xs) = ((x,1+count x xs) : converte xs) 
  where count x [] = 0
        count x (y:ys) = if (x == y) then 1 + count x ys else count x ys

你快到了。

问题出在您对 convert 函数的递归调用上。由于您已经计算了特定字符的字符数,因此不需要再次计算它们。只需使用 filter 函数在调用 convert:

时删除该字符
convert :: Eq a => [a] -> MSet a
convert [] = []
convert (x:xs) = (x,1+count x xs) : convert (filter (\y -> y /= x) xs)
  where count x [] = 0
        count x (y:ys) = if (x == y) then 1 + count x ys else count x ys

或者写得更简洁:

convert :: Eq a => [a] -> MSet a
convert [] = []
convert (x:xs) = (x,1+count x xs) : convert (filter (/= x) xs)
  where count x [] = 0
        count x (y:ys) = if (x == y) then 1 + count x ys else count x ys

ghci 中的演示:

ghci| > convert "bacaba"
[('b',2),('a',3),('c',1)]

what is the better way to do it?

您的代码执行 O(n^2);如果类型是 Ord 类型 class 的实例(如您的示例所示),使用 Data.Map 您可能会获得 O(n log n) 性能:

import Data.Map (toList, fromListWith)

convert :: (Ord a) => [a] -> [(a, Int)]
convert xs = toList . fromListWith (+) . zip xs $ repeat 1

这将导致正确的计数,但列表将按以下键排序:

\> convert "bacaba"
[('a',3),('b',2),('c',1)]

如果需要保留顺序,则

import qualified Data.Map as M
import Data.Map (delete, fromListWith)

convert :: (Ord a) => [a] -> [(a, Int)]
convert xs = foldr go (const []) xs . fromListWith (+) . zip xs $ repeat 1
    where
    go x f cnt = case M.lookup x cnt of
        Just i  -> (x, i): f (x `delete` cnt)
        Nothing -> f cnt

这将输出:

\> convert "bacaba"
[('b',2),('a',3),('c',1)]