F# - 删除字符串中的第一个字符后的重复字符

问题描述:

我想要做的是删除字符串中特定给定字符的重复项,但让第一个字符保留。即:F# - 删除字符串中的第一个字符后的重复字符

let myStr = "hi. my .name." 

//a function that gets a string and the element to be removed in the string 
someFunc myStr "." 

其中someFunc返回如下字符串showen:

"hi. my name" 

这是很容易从一个字符串中删除重复的,但有没有办法删除重复的,但让第一个重复的元素留在字符串中?

+0

不熟悉F#但是,您可以通过迭代字符串来创建字符列表。使用Contains方法来检查一个字符是否已经存在。如果是这样,跳过,否则添加到列表 – Laazo

+0

Azola的想法看起来不错,但我建议做一个集合,因为'List.contains'是O(N)。使用['Set.ofSeq'](https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/set.ofseq%5B't%5D-function-%5Bfsharp%5D),然后你应该能够在O(1)时间内进行查找。 – rmunn

+5

你能告诉我们你试过了什么吗? – TheInnerLight

这里有一个办法:

let keepFirst c s = 
    Seq.mapFold (fun k c' -> (c', k||c<>c'), k&&c<>c') true s 
    |> fst 
    |> Seq.filter snd 
    |> Seq.map fst 
    |> Array.ofSeq 
    |> System.String 

let example = keepFirst '.' "hi. my .name." 

let someFunc (str : string) c = 
    let parts = str.Split([| c |]) 
    if Array.length parts > 1 then 
     seq { 
      yield Array.head parts 
      yield string c 
      yield! Array.tail parts 
     } 
     |> String.concat "" 
    else 
     str 

请注意,该字符是以char而不是字符串形式给出的。

let someFunc chr (str:string) = 
    let rec loop (a: char list) b = function 
     | [] -> a |> List.rev |> System.String.Concat 
     | h::t when h = chr -> if b then loop a b t 
           else loop (h::a) true t 
     | h::t -> loop (h::a) b t 
    loop [] false (str.ToCharArray() |> Array.toList) 

请注意,该字符是以char而不是字符串形式给出的。

编辑:另一种方法是使用正则表达式

open System.Text.RegularExpressions 

let someOtherFunc c s = 
    let pat = Regex.Escape(c) 
    Regex.Replace(s, sprintf "(?<=%s.*)%s" pat pat, "") 

需要注意的是,在这种情况下,字符作为字符串。

编辑2:

let oneMoreFunc (c:char) (s:string) = 
    let pred = (<>) c 
    [ s |> Seq.takeWhile pred 
     seq [c] 
     s |> Seq.skipWhile pred |> Seq.filter pred ] 
    |> Seq.concat 
    |> System.String.Concat 

当设计一个功能,可考虑从制造它的参数一般收益。要通过迭代传递状态,禁止可变变量,Seq.scan可能是一个选择的武器。它折叠成新状态的元组和一个选项,然后Seq.choose去掉状态和不需要的元素。

在功能构建模块方面,使其接受谓词功能'a -> bool并让它返回函数seq<'a> -> seq<'a>。然后

let filterDuplicates predicate = 
    Seq.scan (fun (flag, _) x -> 
     let p = predicate x in flag || p, 
     if flag && p then None else Some x) (false, None) 
    >> Seq.choose snd 

这可以很容易地重用做其他事情,以及像0 together with odd numbers

filterDuplicates (fun i -> i % 2 = 0) [0..10] 
// val it : seq<int> = seq [0; 1; 3; 5; ...] 

提供与对等式操作符的调用,并送入的System.String构造函数,你会得到你想要的签名,char -> seq<char> -> System.String附近。

let filterDuplicatesOfChar what s = 
    System.String(Array.ofSeq <| filterDuplicates ((=) what) s) 
filterDuplicatesOfChar '.' "hi. my .name." 
// val it : string = "hi. my name"