我想知道如何从字符串中删除特定的重复项。一个例子是:
"|Hello|| My|| Name|| Is|| XYZ|"
应该变成:
"|Hello| My| Name| Is| XYZ|"
谢谢
我想知道如何从字符串中删除特定的重复项。一个例子是:
"|Hello|| My|| Name|| Is|| XYZ|"
应该变成:
"|Hello| My| Name| Is| XYZ|"
谢谢
如果您允许自己Data.List.Split
(您应该这样做!),您可以将字符串拆分为单词
splitOn "|" "|Hello|| My|| Name|| Is|| XYZ|"
产生
["","Hello",""," My",""," Name",""," Is",""," XYZ",""]
您要在其中替换所有出现的""
with"|"
然后将单词合并在一起。这只是对 的调用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|" "|"
为了说明我所说的易于修复的意思——假设你想将任意数量的|
s 顺序减少到一个|
. 使用正则表达式解决方案,您只需像这样更改正则表达式:
> subRegex (mkRegex "\\|+") "|||Hello||||||| My|| Name|||| Is|| XYZ|||||" "|"
"|Hello| My| Name| Is| XYZ|"
一个非常简单且相当明显的解决方案是在双头上进行模式匹配:
foo :: Char -> String -> String
foo elem (xa:xb:xs) = ...
然后检查是否xa
等于xb
,然后将它们与其余部分一起返回,或者如果它们重复则只返回其中一个,然后向前移动一个字符。
这里的关键问题是你如何处理两个以上|
的连续。这里提供的解决方案在这个特定方面有很大不同。
您是否将重复数据删除解释||||
为“在另一个 | 之前删除一个 |”,所以,就像迄今为止基于 splitOn 的所有解决方案一样,只会扯掉那个|
,"Hello ||||"
变成"Hello |||"
?
您是否将重复数据删除解释||||
为“将所有 || 对减少为一个 |”,那么它应该转换"Hello ||||"
为"Hello ||"
吗?
您是否将重复数据删除解释||||
为“减少字符串直到只出现单数 | 出现”,所以应该翻译"Hello ||||"
成"Hello |"
?
因此,已经提出了(1)的解决方案。(2) 和 (3) 的解决方案可以以彼此相似的方式构建:
(2) 的解决方案:
dedup c (x:y:xs) | x == c && x == y = x: dedup c xs
dedup c (x:xs) = x: dedup c xs
dedup c _ = []
(3) 的解决方案:
dedup c (x:y:xs) | x == c && x == y = dedup c (y:xs)
dedup c (x:xs) = x: dedup c xs
dedup c _ = []
只需对何时添加找到一对的时间进行细微调整,|
就会导致行为上的巨大差异。
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
获取 type 的函数(a -> a -> Bool)
(我将其称为f
)并遍历列表,一次传入两个元素。如果f
返回True
,则将两个元素放在同一个子列表中,而如果f
返回False
,则创建一个新的子列表。
一种试验方法groupBy
是设置f
为(==)
:
ghci> groupBy (==) "aaabbbcccdeffg"
["aaa","bbb","ccc","d","e","ff","g"]
当元素相等或(==)
返回时,这会将元素组合在一起True
,因此相同的字母会组合在一起。
(顺便说一句,请记住,在 Haskell 中, aString
实际上是 a [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
'|'
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
直接申请,而不使用let
表达式。
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|"