import Data.List
data Encoding = Multiple Int Char | Single Char deriving (Eq,Show,Ord)
游程长度编码
encode :: String -> [Encoding]
encode inputString =encoding (group inputString) []
encoding :: [String] -> [Encoding] -> [Encoding]
encoding groupString xs=
if (length groupString == 0)
then xs
else
case (head groupString) of
([c]) ->encoding (tail groupString) (xs ++ [Single c])
(x) -> encoding (tail groupString) (xs ++ [Multiple (length x) (head x)])
运行长度解码
decode :: [Encoding] -> String
decode listString = decoding listString []
decoding :: [Encoding] -> String -> String
decoding inputString xs=
if (length inputString == 0)
then xs
else
case (head inputString) of
(Single x) ->decoding (tail inputString) (xs++ [x])
(Multiple num x) ->decoding (tail inputString) (xs ++ (replicate num x) )
这是我的运行长度编码解决方案,任何人都可以建议我更好的编写和解码函数的方法
答案 0 :(得分:5)
许多代码专门用于更新累加器。您将元素添加到该累加器的尾部,这将对性能产生巨大影响。
这通常效率不高的原因是因为Haskell [a]
中的列表 - 至少在概念上 - 实现为链表。因此,将l1
和l2
两个列表与l1 ++ l2
连接起来,将采用 O(| l1 |)时间(即l1
中的元素数量{1}})。这意味着如果列表已经包含100个元素,那么在最后添加一个额外元素将需要大量工作。
另一种反模式是使用head
和tail
。是的,可以使用这些函数,但遗憾的是,由于您不使用模式匹配,可能会传递一个空列表,然后head
和tail
将会出错。
此处的另一个问题是您在列表中使用length
。由于有时Haskell中的列表可以具有无限长度,因此length
调用将 - 如果我们需要它 - 永远不会结束。
如果你必须在累加器的末尾附加,通常我们可以在列表的尾部写下递归" cons"我们正在建设。所以我们可以从:
重写我们的程序encode :: String -> [Encoding]
encode [] = []
encode (h:t) = ...
现在的问题是我们如何计算这些价值观。我们可以使用span :: (a -> Bool) -> [a] -> ([a],[a])
,这个函数将 - 对于给定的谓词和列表 - 构造一个2元组,其中第一个项目包含列表的前缀,其中所有元素都满足谓词,第二个项目包含列表的其余部分,我们可以使用它:
encode :: String -> [Encoding]
encode [] = []
encode (h:t) | nh > 1 = Multiple nh h : tl
| otherwise = Single h : tl
where (s1, s2) = span (h ==) t
nh = 1 + length s1
tl = encode s2
例如:
Prelude Data.List> encode "Foobaaar qqquuux"
[Single 'F',Multiple 2 'o',Single 'b',Multiple 3 'a',Single 'r',Multiple 3 ' ',Multiple 3 'q',Multiple 3 'u',Single 'x']
对于解码,我们再次不需要累加器,并且可以使用replicate :: Int -> a -> [a]
:
decode :: [Encoding] -> String
decode [] = []
decode (Single h:t) = h : decode t
decode (Multiple nh h) = replicate nh h ++ decode t
或者我们可以使用列表monad:
decode :: [Encoding] -> String
decode = (>>= f)
where f (Single h) = [h]
f (Multiple nh h) = replicate nh h
例如:
Prelude Data.List> decode [Single 'F',Multiple 2 'o',Single 'b',Multiple 3 'a',Single 'r',Multiple 3 ' ',Multiple 3 'q',Multiple 3 'u',Single 'x']
"Foobaaar qqquuux"
答案 1 :(得分:2)
作为Willem Van Onsem's优秀answer的扩展,请考虑单独创建游程长度,然后将它们与zipWith
的字母组合在一起。
Data.List
具有函数group
(它本身是泛型groupBy
; group = groupby (==)
)的特化,它将字符串分解为同源子字符串。即:
group "aaabbccccd"
= ["aaa", "bb", "cccc", "d"]
计算每个长度将为您提供游程。
N.B。 group
的实现方式与Willem的span
解决方案完全相同。
import Data.List (group)
data Encoding = Multiple Int Char
| Single Char
deriving (Eq, Show, Ord)
encode :: String -> [Encoding]
encode xs = zipWith op lengths letters
where
groups = group xs
lengths = map length groups
letters = map head groups
op :: Int -> Char -> Encoding
op 1 = Single
op n = Multiple n
这也可以作为一个非常难看的列表理解来完成。
encode xs = [ let (n, c) = (length g, head g)
in if n == 1
then Single c
else Multiple n c
| g <- group xs ]
答案 2 :(得分:1)
您的encoding
功能是功能图。不要使用自己的原始递归函数,而只需使用map
。
您的编码正在反转输出(xs ++ [Single c]
等),这既反直觉又昂贵。停止它。
括号太多,例如case (..) of
,if (...) then
以及案例(...) ->
。所有这些都是不必要的,并且使代码混乱。
如果您输入head
,则可能会在某处匹配模式。
考虑:
encoding :: String -> [Encoding]
encoding = map enc . group -- Point 1, use map which also takes
-- care of point 2 and 3.
where
enc [x] = Single x
enc xs@(x:_) = Multiple (length xs) x -- Point 4, patterns not `head`
-- Here consider make pattern matches total either via an error call or Maybe type