Haskell - 从列表中删除相邻的重复项

时间:2018-03-20 13:45:40

标签: list haskell recursion fold

我试图通过解决一些在线问题和训练练习来学习haskell。

现在我正在尝试创建一个能够从列表中删除相邻副本的功能。

  

示例输入

     

" acvvca"

     

" 1456776541"

     

" abbac"

     

" aabaabckllm"

           

预期产出

     

""

     

""

     

" C"

     

" CKM"

我的第一个问题是创建一个函数,只需删除相邻重复项的第一个实例并恢复列表。

module Test where

removeAdjDups :: (Eq a) => [a] -> [a]
removeAdjDups []           =  []
removeAdjDups [x]          =  [x]
removeAdjDups (x : y : ys)
  | x == y = removeAdjDups ys
  | otherwise = x : removeAdjDups (y : ys)
*Test> removeAdjDups "1233213443"
"122133"

此函数适用于首次找到的对。

所以现在我需要对函数的结果应用相同的函数。

我认为foldl可以帮助我,但我不知道我将如何实施它。

中的某些内容
removeAdjDups' xs = foldl (\acc x -> removeAdjDups x acc) xs

这种方法也是实施解决方案的最佳方式,还是我应该考虑更好的方法?

4 个答案:

答案 0 :(得分:3)

从最后一个顺序开始:首先从尾部删除重复项,然后检查输入的头部是否等于尾部结果的头部(此时此刻,它没有任何重复项,因此只有可能的对是输入的头部与尾部结果的头部):

main = mapM_ (print . squeeze) ["acvvca", "1456776541", "abbac", "aabaabckllm"]

squeeze :: Eq a => [a] -> [a]
squeeze (x:xs) = let ys = squeeze xs in case ys of
                                            (y:ys') | x == y -> ys'
                                            _ -> x:ys
squeeze _ = []

输出

""
""
"c"
"ckm"

答案 1 :(得分:2)

我不知道foldl如何用于此。 (通常,foldl几乎结合了foldrfoldl'的缺点......那些或foldMap是您通常应该使用的折叠,而不是{{1} }}。)

您似乎想要的是:重复 foldl,直到找不到重复项。

是重复工作
removeAdjDups

iterate :: (a -> a) -> a -> [a]

这是一个无限缩减列表的无限列表。通常,它将收敛到空列表;你想要添加一些终止条件。如果您想根据需要删除尽可能多的重复项,那就是 fixpoint ;可以通过与实现Prelude> iterate removeAdjDups "1233213443" ["1233213443","122133","11","","","","","","","","","","","","","","","","","","","","","","","","","","",""... 的方式非常相似的方式找到它:比较邻居元素,这次只是在减少列表中。

处理递归重复的

bipll's suggestion要好得多,它避免了不必要的比较并反复遍历列表的开头。

答案 2 :(得分:0)

列表理解经常被忽视。它们当然是语法糖,但有些像我一样沉迷于上瘾。首先,字符串就是列表。这个函数既可以处理任何列表,也可以处理单例和空列表。您可以映射以处理列表中的许多列表。

(\l -> [ x  | (x,y) <- zip l $ (tail l) ++ " ", x /= y]) "abcddeeffa"

“abcdefa”

答案 3 :(得分:0)

我没有看到如何使用foldl。这可能是因为,如果你想在这里折叠一些东西,你必须使用foldr

main = mapM_ (print . squeeze) ["acvvca", "1456776541", "abbac", "aabaabckllm"]

-- I like the name in @bipll answer
squeeze = foldr (\ x xs -> if xs /= "" && x == head(xs) then tail(xs) else x:xs) ""

让我们分析一下。这个想法取自@bipll回答:从右到左。如果f是lambda函数,那么根据foldr

的定义
squeeze "abbac" = f('a' f('b' f('b' f('a' f('c' "")))

根据f定义f('c' "") = 'c':"" = "c"xs == ""。右边的下一个字符:f('a' "c") = 'a':"c" = "ac"'a' != head("c") = 'c'以来。 f('b' "ac") = "bac"出于同样的原因。但是f('b' "bac") = tail("bac") = "ac"因为'b' == head("bac")。等等......

奖励:将foldr替换为scanr,您可以看到整个过程:

Prelude> squeeze' = scanr (\ x xs -> if xs /= "" && x == head(xs) then tail(xs) else x:xs) ""
Prelude> zip "abbac" (squeeze' "abbac")
[('a',"c"),('b',"ac"),('b',"bac"),('a',"ac"),('c',"c")]