F# - удалить повторяющиеся символы после первого в строке

То, что я пытаюсь сделать, - это удалить дубликаты конкретного заданного символа в строке, но оставить первый символ. То есть:

let myStr = "hi. my .name."

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

где someFunc возвращает показанную ниже строку:

"hi. my name"

Удалить дубликаты из строки легко, но есть ли способ удалить дубликаты, но оставив первый дублированный элемент в строке?

4 ответа

Решение

Вот один из подходов:

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

Обратите внимание, что символ задается как символ вместо строки.

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)

Обратите внимание, что символ задается как символ вместо строки.

Изменить: Другой способ будет использовать регулярные выражения

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 вместе с нечетными числами.

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"
Другие вопросы по тегам