我试图通过解决一些在线问题和训练练习来学习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
这种方法也是实施解决方案的最佳方式,还是我应该考虑更好的方法?
答案 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
几乎结合了foldr
和foldl'
的缺点......那些或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")]