haskell初学者字符串

时间:2018-10-30 01:45:28

标签: haskell

我想编写一个简单的字符串函数:

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之外,我别无选择。

2 个答案:

答案 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
  • 已分组的元素被一一映射, 进行lambda计算,对于每个字符, 计算其数量: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

现在呢?我们知道currest都是字符串,并且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 []          = []