作为项目的一部分,我将自己指定为提高我对 F# 和函数式编程的一般知识的一种方式,我正在尝试从头开始编写字符串模式匹配算法,而不使用任何循环或变量(或正则表达式或字符串.替换和朋友)。由于这纯粹是一个学习项目,我对最好的方法不感兴趣,只对最好的功能方法感兴趣。
我正在尝试编写一个接受通配符、模式字符串和输入字符串作为参数的函数。如果模式与输入不匹配,则函数返回None
. 如果模式与输入匹配,则该函数返回输入字符串Some(str)
的str
任何部分与模式字符串中可能存在的任何通配符匹配的位置。
我大部分时间都在工作,稍后我会包含代码。我编写了一个通用模式匹配函数,它适用于任何支持相等的通用列表,然后是一个辅助函数,它接受字符串并将字符列表传递给通用函数。这一切都有效,除了一件事:模式字符串中对多个通配符的支持不是很好 - 它获取每个通配符的匹配项并将它们连接到输出中的单个字符串中。
例如:
> strMatch '*' "foo" "bar";;
val it : string option = None
> strMatch '*' "test" "test";;
val it : string option = Some ""
> strMatch '*' "functional programming is *" "functional programming is fun";;
val it : string option = Some "fun"
> strMatch '*' "* and *" "you and me";;
val it : string option = Some "youme"
这是我要修复的最后一个。理想情况下,我想返回一个字符串列表而不是单个字符串,列表中的每个元素都是与一个通配符匹配的字符串。如果做不到这一点,我可能会使用只返回第一个通配符匹配的版本 - 这是我需要摆脱的两个通配符的连接值。我只是不太确定如何处理它。
因此,如果有人可以建议我如何根据它们匹配的通配符对返回值进行分组,我将不胜感激。我也对您可能想要建议的对我的代码的任何其他改进感兴趣。
let rec doMatch (wildcard:'a) (pat:'a list) (input:'a list) : 'a list option =
let singleMatch p i =
match (p, i) with
| phd :: ptl, ihd :: itl ->
if phd = wildcard then
match doMatch wildcard ptl itl with
| None -> None
| Some x -> Some(ihd :: x)
else None
| _ -> None
let longerMatch p i =
match (p, i) with
| phd :: ptl, ihd :: itl ->
if phd = wildcard then
match doMatch wildcard p itl with
| None -> None
| Some x -> Some(ihd :: x)
else None
| _ -> None
match (pat, input) with
| [], [] -> Some([])
| [], _::_ -> None
| _::_, [] -> None
| phd :: ptl, ihd :: itl ->
if phd <> wildcard then
if phd = ihd then doMatch wildcard ptl itl
else None
else
match singleMatch pat input with
| Some x -> Some(x)
| None -> longerMatch pat input
let strMatch (wildcard:char) (pat:string) (input:string) =
match doMatch wildcard (List.ofSeq pat) (List.ofSeq input) with
| None -> None
| Some x -> Some(new string(Array.ofList x))
您可能已经猜到了,但这是 F# 中 Eliza 聊天机器人实现的一部分。