F# - 从字符数组中删除后续重复项(无序)

时间:2011-01-28 12:47:54

标签: f# soundex

我正在努力学习F#。我需要一个简单的soundex表达式的帮助。 我正在使用Simplified(也称为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

目前我被困在规则号上。我正在考虑使用递归表达式。 由于我目前是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()

4 个答案:

答案 0 :(得分:4)

如果要删除后续重复项( zeuxcg 的解决方案中的第二个选项),那么您也可以直接将其实现为递归函数(使用 accumulator参数)。这很好地展示了模式匹配,所以在学习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 |]

作为旁注,我发现你的语法有点难以辨认。我不认为写... |> fun s -> ...是个好主意,因为它只是let s = ... in ...的模糊版本。我建议写一些类似的东西(我不确定我是否完全理解你的代码,但你明白了......):

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)

答案 1 :(得分: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]|]

答案 2 :(得分:1)

如果要从数组中删除所有重复项(留下唯一元素),以下操作将会执行以下操作:

arr |> Seq.distinct |> Seq.toArray

如果要删除连续的重复项,则解决方案更难。这是我能想到的最简单的一个:

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

您可以通过Array.toListArray.ofList或使用Array.fold并更改匹配表达式和列表构建来使用数组执行此操作;代码不太可读,所以我发布了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))

答案 3 :(得分: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文章。