F# - Entfernen Sie nachfolgende Duplikate aus einer Reihe von Zeichen (ungeordnet)

StackOverflow https://stackoverflow.com/questions/4828369

  •  26-10-2019
  •  | 
  •  

Frage

Ich versuche f#zu lernen. Und ich brauche etwas Hilfe bei einem einfachen Soundex -Ausdruck. Ich verwende den folgenden Regeln für vereinfachte (auch als American) SoundEx:

1.) Assign characters to classes
2.) Remove duplicate values here, e.g. 222 becomes 2  
3.) Replace first encoded char with first char  
4.) Remove nulls
5.) Truncate ot pad to totally 4 characters

Derzeit stecke ich an der Regel Nr. 2. Ich dachte daran, einen rekursiven Ausdruck zu verwenden. Da ich derzeit ein N00B auf F# bin, werde ich versuchen, Sie um eine elegante Lösung für mein Problem zu fragen.

Alle Vorschläge werden sehr geschätzt :)

Hier ist mein Code:

let Simplified (name:string) =
let ca = name.ToLower().ToCharArray()
new string(
    Array.map(
        fun e ->
        match e with                                                          
            | 'a' | 'e' | 'i' | 'o' | 'u' | 'y' | 'w' | 'h' -> '0'
            | 'b' | 'f' | 'p' | 'v'                         -> '1'
            | 'c' | 's' | 'k' | 'g' | 'j' | 'q' | 'x' | 'z' -> '2'
            | 'd' | 't'                                     -> '3'
            | 'l'                                           -> '4'
            | 'm' | 'n'                                     -> '5'
            | 'r'                                           -> '6'
            |  _                                            -> ' '
        )  ca
  //|> fun s -> TODO: Remove duplicates here
    |> fun s -> Array.set s 0 (ca.[0]) 
                Array.choose(fun e -> if e <> '0' then Some(e) else None) s   
)  
|> fun s -> (
            match s.Length with                                               
                | x when x < 3 -> s.PadRight(4, '0')
                | _ -> s.Substring(0, 4)
            ).ToUpper()
War es hilfreich?

Lösung

Seq.fold ist dein Freund.

let soundex (text : string) = 
    let choose = 
        function 
        | 'b' | 'f' | 'p' | 'v' -> Some "1" 
        | 'c' | 'g' | 'j' | 'k' | 'q' | 's' | 'x' | 'z' -> Some "2" 
        | 'd' | 't' -> Some "3" 
        | 'l' -> Some"4" 
        | 'm' | 'n'  -> Some "5"
        | 'r' -> Some "6"
        | _ -> None 

    let fold state value = 
        match state with
        | i :: _ when i = value -> state
        | _ -> value :: state

    let t = text.Substring(1).ToLower() |> Seq.choose choose |> Seq.fold fold [] |> Seq.toList |> List.rev |> String.concat ""

    text.Substring(0,1) + t.PadRight(3, '0').Substring(0, 3)

Dies basiert auf dem Wikipedia -Artikel für SoundEx.

Andere Tipps

Wenn Sie konsequente Duplikate entfernen möchten (die zweite Option in der Zeuxcg's Lösung), dann können Sie dies auch direkt als rekursive Funktion (verwenden Akkumulatorparameter). Dies demonstriert das Muster -Matching, daher ist es gut, wenn man F#lernt:

let removeConsequentDuplicates list = 
  let rec loop acc list =
    match list with 
    | x1::x2::xs when x1 = x2 -> loop acc (x2::xs)
    | x::xs -> loop (x::acc) xs
    | _ -> acc |> List.rev
  loop [] list

Diese Version funktioniert mit Listen, aber da Sie mit Arrays arbeiten, benötigen Sie wahrscheinlich eine imperative Version. Sie können Sequenzausdrücke wie diese verwenden:

let removeConsequentDuplicates (arr:_[]) = 
  let rec loop last i = seq {
    if i < arr.Length - 1 && last = arr.[i] then 
      yield! loop last (i+1)
    elif i < arr.Length - 1 then
      yield arr.[i]
      yield! loop (arr.[i]) (i + 1) }
  [| if arr.Length > 0 then
       yield arr.[0]
       yield! loop arr.[0] 0 |]

Als Seitenstart finde ich Ihre Syntax etwas unlesbar. Ich denke nicht, dass es eine gute Idee ist, zu schreiben ... |> fun s -> ..., weil es nur eine verdeckte Version von ist let s = ... in .... Ich würde empfehlen, so etwas wie zu schreiben (ich bin nicht sicher, ob ich Ihren Code vollständig verstehe, aber Sie haben die Idee ...):

let Simplified (name:string) =
  let ca = name.ToLower().ToCharArray()
  let s = 
    ca |> Array.map (function
            | '0' ... )
       |> removeConsequentDuplicates
  Array.set s 0 (ca.[0])
  let s = s |> Array.choose(fun e -> if e <> '0' then Some(e) else None)
  let s = (new String(s)).ToUpper()
  match s.Length with                                               
  | x when x < 3 -> s.PadRight(4, '0')
  | _ -> s.Substring(0, 4)

Entfernen Sie konsekutive Duplikate mit Arrays mithilfe von Loops anstelle von Rekursion, die meisten einfach in einem Sequenzausdruck wie folgt:

let removeDuplicates (xs: _ []) =
  [|if xs.Length > 0 then yield xs.[0]
    for i=1 to xs.Length-1 do
      if xs.[i] <> xs.[i-1] then
        yield xs.[i]|]

Wenn Sie alle Duplikate aus dem Array entfernen möchten (einzigartige Elemente hinterlassen), wird Folgendes folgen:

arr |> Seq.distinct |> Seq.toArray

Wenn Sie aufeinanderfolgende Duplikate entfernen möchten, ist die Lösung schwieriger. Dies ist das einfachste, das ich mir einfallen lassen kann:

let unique list =
    list
    |> List.fold (fun acc e ->
        match acc with
        | x::xs when x = e -> acc
        | _ -> e::acc) []
    |> List.rev

Sie können es entweder mit Arrays durch Array.toList und Array.ofList oder verwenden Array.fold und Änderung des Match -Ausdrucks und der Listenkonstruktion; Der Code ist weniger lesbar, sodass ich die Listenversion veröffentliche.

Alternative Lösungen beinhalten Seq.pairwise, dh:

let unique arr =
    if Array.isEmpty arr then
        arr
    else
        Array.append [|arr.[0]|] (
            arr
            |> Seq.pairwise
            |> Seq.toArray
            |> Array.choose (fun (p, n) -> if p = n then None else Some n))
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top