如何从字符串中删除彼此相邻的特定重复项?

时间:2013-09-18 11:09:09

标签: string haskell filter duplicates

我想知道如何从字符串中删除特定的重复项。一个例子是:

"|Hello|| My|| Name|| Is|| XYZ|"

应该成为:

"|Hello| My| Name| Is| XYZ|"

由于

5 个答案:

答案 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)

这里的关键问题是你连续两个|做了什么。这里提供的解决方案在这方面有很大的不同。

  1. 您是否将||||的重复数据删除解释为“在另一个之前移除| |”,因此,就像迄今为止基于splitOn的所有解决方案一样,只会删除|,转向{ {1}}进入"Hello ||||"

  2. 您是否将"Hello |||"的重复数据删除解释为“将所有||减少为一个|”,那么它应该将||||转换为"Hello ||||"吗?

    < / LI>
  3. 您是否将"Hello ||"的重复数据删除解释为“只删除字符串直到奇异”,所以应将||||翻译成"Hello ||||"

  4. 因此,已经提出了(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|"