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"