我想知道如何从字符串中删除特定的重复项。一个例子是:
"|Hello|| My|| Name|| Is|| XYZ|"
应该成为:
"|Hello| My| Name| Is| XYZ|"
由于
答案 0 :(得分:4)
如果您允许自己Data.List.Split
(您应该这样做!),您可以将字符串拆分为单词
splitOn "|" "|Hello|| My|| Name|| Is|| XYZ|"
产生
["","Hello",""," My",""," Name",""," Is",""," XYZ",""]
您希望将所有""
替换为"|"
,然后将这些词合并在一起。这只是对concatMap
的调用,如下所示:
concatMap (\s -> if s == "" then "|" else s) $
splitOn "|" "|Hello|| My|| Name|| Is|| XYZ|"
产生
"|Hello| My| Name| Is| XYZ|"
另一种选择是在"||"
上拆分并将部分连接在一起,同时在其间插入"|"
。这只是
intercalate "|" $ splitOn "||" "|Hello|| My|| Name|| Is|| XYZ|"
另一种替代方案,可以说最容易解决的是,如果它出现奇怪的边缘情况,那就是使用正则表达式。它看起来像这样:
subRegex (mkRegex "\\|\\|") "|Hello|| My|| Name|| Is|| XYZ|" "|"
通过易于修复来表明我的意思 - 想象一下,您希望将任意数量的|
依次减少到一个|
。使用正则表达式解决方案,您只需更改正则表达式:
> subRegex (mkRegex "\\|+") "|||Hello||||||| My|| Name|||| Is|| XYZ|||||" "|"
"|Hello| My| Name| Is| XYZ|"
答案 1 :(得分:2)
一个非常简单而且相当明显的解决方案是双头模式匹配:
foo :: Char -> String -> String
foo elem (xa:xb:xs) = ...
然后检查xa
是否等于xb
,并将它们与其余部分一起返回,如果它们是重复的,则返回其中一个,然后向前移动一个字符。
答案 2 :(得分:1)
这里的关键问题是你连续两个|
做了什么。这里提供的解决方案在这方面有很大的不同。
您是否将||||
的重复数据删除解释为“在另一个之前移除| |”,因此,就像迄今为止基于splitOn的所有解决方案一样,只会删除|
,转向{ {1}}进入"Hello ||||"
?
您是否将"Hello |||"
的重复数据删除解释为“将所有||减少为一个|”,那么它应该将||||
转换为"Hello ||||"
吗?
您是否将"Hello ||"
的重复数据删除解释为“只删除字符串直到奇异”,所以应将||||
翻译成"Hello ||||"
?
因此,已经提出了(1)的解决方案。 (2)和(3)的解决方案可以以类似的方式构建:
(2)的解决方案:
"Hello |"
(3)的解决方案:
dedup c (x:y:xs) | x == c && x == y = x: dedup c xs
dedup c (x:xs) = x: dedup c xs
dedup c _ = []
只有在发现一对时附加dedup c (x:y:xs) | x == c && x == y = dedup c (y:xs)
dedup c (x:xs) = x: dedup c xs
dedup c _ = []
时才会略微调整,这会导致行为发生重大差异。
答案 3 :(得分:-2)
ghci> :m Data.List
ghci> let myGroupFunc = groupBy (\a b -> a == '|' && b == '|')
ghci> map head $ myGroupFunc "|Hello|| My|| Name|| Is|| XYZ|"
"|Hello| My| Name| Is| XYZ|"
ghci>
groupBy
的类型为(a -> a -> Bool) -> [a] -> [[a]]
。它需要一个函数和一个列表,并返回一个列表列表。 groupBy
采用类型(a -> a -> Bool)
的函数(我将其称为f
)并遍历列表,传递一个时间的两个元素。如果f
返回True
,那么这两个元素将放在同一个子列表中,而如果f
返回False
,则会创建一个新的子列表。
尝试groupBy
的一种方法是将f
设置为(==)
:
ghci> groupBy (==) "aaabbbcccdeffg"
["aaa","bbb","ccc","d","e","ff","g"]
当元素相等或(==)
返回True
时,它们会将元素组合在一起,因此相同的字母会组合在一起。
(另外,请记住,在Haskell中,String
实际上是[Char]
,因此"aaabbbcccdeffg"
的等效表示形式为:['a','a','a','b','b','b','c','c','c','d','e','f','f',g']
和结果的等效表示是
[['a','a','a'],['b','b','b'],['c','c','c'],['d'],['e'],['f','f'],['g']]
。)
现在让我们在您的示例输入上尝试groupBy (==)
:
ghci> groupBy (==) "|Hello|| My|| Name|| Is|| XYZ|"
["|","H","e","ll","o","||"," ","M","y","||"," ","N","a","m","e","||"," ","I","s","||"," ","X","Y","Z","|"]
请注意,它将元素组合在一起,每次它们都是相同的。但这不是你想要的,因为上面也在"ll"
中将"Hello"
组合在一起。
因此,当一对元素相同时,我们将传递给groupBy
的函数更改为仅返回True
和它们是您想要的字符:{{1 }}:
'|'
请注意,它只会将您想要的角色组合在一起,ghci> groupBy (\a b -> a == '|' && b == '|') "|Hello|| My|| Name|| Is|| XYZ|"
["|","H","e","l","l","o","||"," ","M","y","||"," ","N","a","m","e","||"," ","I","s","||"," ","X","Y","Z","|"]
。现在,由于我们只需要一个重复的元素,我们可以只取每个'|'
的第一个Char
并将它们组合起来得到我们的结果:
String
此答案顶部的解决方案是什么,接受我们直接应用ghci> map head $ groupBy (\a b -> a == '|' && b == '|') "|Hello|| My|| Name|| Is|| XYZ|"
"|Hello| My| Name| Is| XYZ|"
,而不使用f
表达式。
答案 4 :(得分:-2)
import Data.List.Split(splitOn)
removeDup d = concat . map rep . splitOn d
where
rep s = if null s then d else s
> removeDup "|" "|Hello|| My|| Name|| Is|| XYZ|"
"|Hello| My| Name| Is| XYZ|"