Haskell初学者级功能实现

时间:2019-04-09 17:45:09

标签: haskell recursion

我是Haskell的新手,正在尝试编写一个非常简单的函数,该函数将每个重复的连续元素收集在单独的子列表下,例如:

f :: Eq a => [a] -> [[a]]

所以:

f [] = []
f [3] = [[3]]
f [1,1,1,3,2,2,1,1,1,1] = [[1,1,1],[3],[2,2],[1,1,1,1]]

我考虑过此功能:

f :: Eq a => [a] -> [[a]]
f [] = []
f (x:[]) = [[x]]
f (x:x':xs) = if x == x' then [[x, x']] ++ (f (xs))
                    else [[x]] ++ (f (xs))

它似乎不能很好地工作,因为当它到达最后一个元素时,它想将其与连续的元素进行比较,后者显然不存在。

我想收到一个简单的答案(初学者水平),该答案不会与我的有太大不同,纠正我的代码将是最好的。

谢谢。

2 个答案:

答案 0 :(得分:2)

问题实际上并不是您所说的,只是您仅硬编码了一个或两个连续元素相等的情况。实际上,您想将任意个相等的连续部分连在一起。 IOW,对于每个元素,您都会弹出相等数量的后续元素。

通常,满足某些条件的列表的开头部分的拆分是span函数的作用。在这种情况下,应检查的条件是等于您已删除的元素。这样写:

f [] = []
f (x:xs) = (x:xCopies) : f others
 where (xCopies,others) = span (==x) xs

在这里,x:xCopies将等于x的元素块放在一起(x本身在前面),将其用作结果的标题块列表,然后您递归剩余的所有元素。

答案 1 :(得分:0)

您的问题是if的两半都具有相同的结构:它们恰好将一个元素限制在递归调用的前面。这可能是不对的:有时候您想将元素添加到列表的最前面,而其他时候您想将元素与递归调用中已有的元素组合在一起。

相反,您需要在递归调用上进行模式匹配,以获取递归结果中的第一项,然后在前两项匹配时在那个之前。

f :: Eq a => [a] -> [[a]]
f [] = []
f [x] = [[x]]
f (x:xs@(y:_)) | x == y = (x:head):more
               | otherwise = [x]:result
  where result@(head:more) = f xs