2011-01-28 1 views
1

私はF#を学ぼうとしています。そして私は簡単なsoundex表現でsomヘルプが必要です。F# - 文字の配列から後続の重複を削除する(順序なし)

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 

現在、私はノールールに引っかかっています: 私は(もアメリカンと呼ばれる)簡体字については、以下のルールセットのsoundexを使用しています。 2.私は再帰式を使うことを考えていました。 私は現在F#のn00bですので、私の問題に対する洗練された解決策を求めてみるつもりです。テキストをsoundexに翻訳する私の全アプローチは目標外でしょうか?

任意の提案は大歓迎されます:)ここで

が私のコードです:

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() 

答えて

1

Seq.foldはあなたの友人です。

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) 

これは、soundexのwikipediaの記事に基づいています。

1

あなたは、配列(ユニークな要素を残して)からすべての重複を削除したい場合は、次のように行います:

連続した重複を削除する場合、解決策はより難しくなります。これは私が思い付くことができ、最も簡単なものです:あなたは配列Array.toListArray.ofList経由またはArray.foldを使用し、一致式とリストの構築を変更するのいずれかでそれを行うことができます

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

。コードはあまり読みにくいので、Listバージョンを投稿しています。

代替ソリューションはすなわち、Seq.pairwiseを伴う:

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)) 
+0

NB:これらはリストのソートのみで動作します... – Massif

+0

はい、私は、タスクが連続する重複を削除することを前提としていました。さもなければ解決策はちょうどSeq.distinct |> Seq.toArray - 私はポストを更新します。 – zeuxcg

+0

貧しい無視されたfoldBackを忘れないでください。 – gradbot

4

あなたは、その結果として重複(zeuxcgの溶液中で2番目のオプションを)削除したい場合、あなたはまた、再帰関数として直接これを実装することができます(アキュムレータパラメータを使用)。これがうまくパターンマッチングを示し、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 

このバージョンでは、リストで動作しますが、あなたは配列を操作していることから、あなたはおそらく不可欠バージョンが必要です。次のようなシーケンス式を使用できます。

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

補足として、あなたの構文は少し読めません。私はそれがlet s = ... in ...のちょうど隠れたバージョンであるので、... |> fun s -> ...を書くことは良い考えではないと思う。 (...私は完全にあなたのコードを理解していないが、あなたのアイデアを得る)私のような何かを書くことをお勧めします:

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) 
2

が最も単純ではなく、再帰よりも、ループを使って配列を使用して、連続する重複を削除します

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]|] 
関連する問題