F # - rimuovere i duplicati successivi da un array di caratteri (non ordinato)

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

  •  26-10-2019
  •  | 
  •  

Domanda

Sto cercando di imparare F #. E ho bisogno di aiuto som con una semplice espressione soundex. Sto usando il seguente set di regole per semplificato (chiamato anche americano) 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

Al momento mi sono bloccato sulla norma n. 2. Stavo pensando di usare un'espressione ricorsiva. Come io sono attualmente un n00b su F # cercherò di chiedere per una soluzione elegante al mio problem.Maybe tutto il mio approccio per tradurre il testo di soundex è fuori?

Qualche suggerimento sarà molto apprezzato:)

Ecco il mio codice:

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()
È stato utile?

Soluzione

Seq.fold è tuo amico.

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)

Questo è basato sull'articolo Wikipedia per soundex.

Altri suggerimenti

Se si desidera rimuovere conseguenti duplicati (la seconda opzione in zeuxcg 's soluzione), allora si può anche implementare questo direttamente come una funzione ricorsiva (con accumulatore parametro ). Questo dimostra ben pattern matching, quindi è buona cosa da provare, mentre l'apprendimento F #:

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

Questa versione funziona con le liste, ma dal momento che si sta lavorando con gli array, sarà probabilmente bisogno di una versione imperativo. È possibile utilizzare espressioni di sequenza in questo modo:

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 |]

Come un lato nota, trovo la sintassi un po 'illeggibile. Non credo che sia una buona idea per ... |> fun s -> ... scrittura, perché è solo una versione oscurata di let s = ... in .... Suggerirei a scrivere una cosa del genere (io non sono sicuro di capire pienamente il codice, ma si ottiene l'idea ...):

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)

Rimuovi duplicati consecutivi usando array utilizzando i cicli anziché ricorsione, più semplicemente in un'espressione sequenza come questa:

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]|]

Se si desidera rimuovere tutti i duplicati dalla matrice (lasciando elementi unici), il seguente farà:

arr |> Seq.distinct |> Seq.toArray

Se si desidera rimuovere i duplicati consecutivi, allora la soluzione è più difficile. Questo è quello più semplice che posso venire con:

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

E 'possibile farlo con gli array sia via Array.toList e Array.ofList o utilizzando Array.fold e cambiare espressione partita e costruzione di lista; il codice è meno leggibile quindi sto postando la versione lista.

Le soluzioni alternative coinvolgono Seq.pairwise, cioè:.

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))
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top