我想编写一个简单的字符串函数:
input: aaabbbAAAcccc
output: a3b3A3c4
我现在拥有的这段代码:
compress :: String -> String
compress x = if length x > 1
then do take 1 x ++ show ( length $ filter (==head x) x) ++ compress (drop 1 x)
else x
这显然行不通。输出如下:
*Main> compress "aaaaaaaaabbbbbbb"
"a9a8a7a6a5a4a3a2a1b7b6b5b4b3b2b"
我要从x字符串应用过滤器中取出1,以取出字符串中有多少个“ head x”,然后为每个字符输出它,然后在我从x中删除1后递归调用它。
我知道这是错误的方法,因此无法编写。但是,如何跟踪String中有多少个相等字符以及如何递归比较它们?我必须使用类似的东西吗?
compress :: String -> String
compress (x:xs) =
然后调用xs并以某种方式将其与x进行比较,或者除了尝试理解Data.List之外,我别无选择。
答案 0 :(得分:4)
简单的解决方案
解决此问题的一种简单方法是应用以下功能:
Prelude> let str = "aaabbbAAAcccc"
Prelude> concat $ fmap (\x -> [head x]<>show(length x)) (group str)
Prelude> "a3b3A3c4"
所以您的压缩功能将是:
compress :: String -> String
compress str = concat $ fmap (\x -> [head x]<>show(length x)) (group str)
建议
在解决问题时采用更实用的思维方式会有所帮助 Haskell的问题。如果-那么-其他句子很少见,使用 在许多情况下,lambdas函数的简化很方便 代码的可读性,函数映射和递归 Haskell世界的经典作品。
工作方式 所述的compress函数执行以下操作:
group str
fmap (\x -> [head x]<>show(length x))
concat
原始代码为什么不起作用?
问题中出现的代码基本上无法正常工作,因为该算法进入的是贯穿字符列表的递归,并且对于每个字符,一次又一次地计数相同的字符,在字符串的位置前进而没有考虑到部分相似字符被计算出来。
另一方面,可以正确使用列表模式(x:xs)滚动列表。 x表示头部,xs表示递归列表的尾部。
答案 1 :(得分:4)
group
是一个自然的赢家,采用了"Mississippi"
这样的字符串并将其变成["M", "i", "ss", "i", "ss", "i", "pp", "i"]
,但是让我们想象一下您不想这样做。您如何通过更原始的显式递归来实现这一点?
首先,您有takeWhile
。
takeWhile :: (a -> Bool) -> [a] -> [a]
给出一个谓词和一个列表,只要它们都满足该谓词,就从左边开始尽可能多地获取连续的元素
takeWhile (>3) [4, 5, 6, 9, 9, 5, 3, 5, 4] = [4, 5, 6, 9, 9, 5] -- loses [3, 5, 4]
甚至更好的是,您可以拥有span
,它可以完成和的所有工作。
span :: (a -> Bool) -> [a] -> ([a], [a])
span (>3) [4, 5, 6, 9, 9, 5, 3, 5, 4] = ([4, 5, 6, 9, 9, 5], [3, 5, 4])
这应该使您可以抽出每个具有相同字母的部分。
-- here I'm using the @ notation to pattern match on `x` without losing the larger pattern that I'm naming xss
compress (xss@(x:_)) = let (cur, rest) = span (==x) xss
现在呢?我们知道cur
和rest
都是字符串,并且cur
是从左边开始所有相等的连续字母。好吧,我想我们可以将它们加起来并放在其中一个前面。
compress (xss@(x:_)) = let (cur, rest) = span (==x) xss
in x:(show . length $ cur) ++ ???
我们如何处理这些问号?我们其余的代码是什么?好吧,我们将不得不以某种方式处理rest
。我们希望rest
发生什么?好吧,我们也希望对其进行压缩。让我们尝试仅在此处递归。
compress (xss@(x:_)) = let (cur, rest) = span (==x) xss
in x:(show . length $ cur) ++ compress rest
这看起来很有希望,但我们缺少递归的秘密之作-基本案例。元素用完了怎么办?当我们调用compress []
时,我们需要做一些 事,那件事应该在我们(:)
链的末端结束。自然的东西是[]
。
compress :: String -> String
compress (xss@(x:_)) = let (cur, rest) = span (==x) xss
in x:(show . length $ cur) ++ compress rest
compress [] = []