我正在努力学习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()
答案 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.toList
和Array.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文章。