1

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

目前我被困在规则没有。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()
4

4 回答 4

4

如果要删除后续重复项(zeuxcg解决方案中的第二个选项),那么您也可以直接将其实现为递归函数(使用累加器参数)。这很好地演示了模式匹配,因此在学习 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)
于 2011-01-28T16:40:32.893 回答
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]|]
于 2011-01-30T22:08:21.007 回答
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和更改匹配表达式和列表构造来处理数组;代码可读性较差,因此我发布了列表版本。

替代解决方案涉及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))
于 2011-01-28T13:26:21.740 回答
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 的维基百科文章。

于 2011-01-31T09:59:21.093 回答