我是否正确使用了函数组合?
为了理解函数式编程的功能,我整理了一些基本函数,您可以将它们组合在一起来构建复杂的正则表达式。现在,经过一些测试,我发现这是可行的,但是您可以用任何可行的语言编写一些可怕的代码。这是专业 F# 程序员编写的代码吗?还是我滥用了该功能?
注意: test
正是我所指的。
type State = { input:string; index:int; succeeded:bool }
type Matcher = State -> State
let term (cs:char Set) =
fun s ->
if s.succeeded && s.index < s.input.Length && cs.Contains s.input.[s.index] then
{ input = s.input; index = s.index + 1; succeeded = true }
else
{ input = s.input; index = s.index; succeeded = false }
let quantify (term, min, max) =
let rec inner (s:State, count) =
if s.succeeded && s.index < s.input.Length && count <= max then
inner (term { input = s.input; index = s.index + 1; succeeded = true }, count + 1)
elif count >= min && count <= max then
{ input = s.input; index = s.index - 1; succeeded = true }
else
s
fun s -> inner (s, 0)
let disjunction leftTerm rightTerm =
fun s ->
let left = leftTerm s
if not left.succeeded then
let right = rightTerm s
if not right.succeeded then
{ input = s.input; index = s.index; succeeded = false }
else
right
else
left
let matcher input terms =
let r = terms { input = input; index = 0; succeeded = true }
if r.succeeded then r.input.Substring (0, r.index) else null
let test = // (abc|xyz)a{2,3}bc
disjunction // (abc|xyz)
(term (set "a") >> term (set "b") >> term (set "c"))
(term (set "x") >> term (set "y") >> term (set "z"))
>> quantify (term (set "a"), 2, 3) // (a{2,3})
>> term (set "b") // b
>> term (set "c") // c
let main () : unit =
printfn "%s" (matcher "xyzaabc" test)
System.Console.ReadKey true |> ignore
main()
In an effort to understand the capabilities of functional programming I put together a few basic functions that you can compose together to build complex regular expressions. Now after some testing I have found this works but you can write some horrible code in any language that will work. Is this the kind of code you would find a professional F# programmer writing or am I abusing the feature?
Note: test
is specifically what I am referring to.
type State = { input:string; index:int; succeeded:bool }
type Matcher = State -> State
let term (cs:char Set) =
fun s ->
if s.succeeded && s.index < s.input.Length && cs.Contains s.input.[s.index] then
{ input = s.input; index = s.index + 1; succeeded = true }
else
{ input = s.input; index = s.index; succeeded = false }
let quantify (term, min, max) =
let rec inner (s:State, count) =
if s.succeeded && s.index < s.input.Length && count <= max then
inner (term { input = s.input; index = s.index + 1; succeeded = true }, count + 1)
elif count >= min && count <= max then
{ input = s.input; index = s.index - 1; succeeded = true }
else
s
fun s -> inner (s, 0)
let disjunction leftTerm rightTerm =
fun s ->
let left = leftTerm s
if not left.succeeded then
let right = rightTerm s
if not right.succeeded then
{ input = s.input; index = s.index; succeeded = false }
else
right
else
left
let matcher input terms =
let r = terms { input = input; index = 0; succeeded = true }
if r.succeeded then r.input.Substring (0, r.index) else null
let test = // (abc|xyz)a{2,3}bc
disjunction // (abc|xyz)
(term (set "a") >> term (set "b") >> term (set "c"))
(term (set "x") >> term (set "y") >> term (set "z"))
>> quantify (term (set "a"), 2, 3) // (a{2,3})
>> term (set "b") // b
>> term (set "c") // c
let main () : unit =
printfn "%s" (matcher "xyzaabc" test)
System.Console.ReadKey true |> ignore
main()
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
该代码对我来说看起来相当不错。
我不确定这是您的意图还是巧合,但您正在实现与“解析器组合器”非常相似的东西,这是许多学术论文的主题:-)。我认为 Monadic Parser Combinators 非常可读(它有示例Haskell,但您应该能够将它们转换为 F#)。
关于函数复合运算符。我通常不太喜欢过多使用运算符,因为它经常会混淆代码。但是,在您的示例中,它很有意义,因为您可以轻松想象
>>
表示“该组应该跟随该组”,这很容易解释。我要做的唯一的小改变是为析取操作选择一些不错的自定义运算符并定义一些更原始的操作,以便您可以编写这样的示例:
这是更高级的描述,因此它删除了一些
>>
运算符,转而使用更具描述性的函数(并封装>>
)。我还更改了quantify
以采用多个参数而不是三元组(这是一个微小的更改)如果您想进一步研究这个,那么您可以看一下这篇文章并尝试编写 F# 计算表达式生成器允许您使用
parser { .. }
语法。The code looks pretty good to me.
I'm not sure if this was your intention or a coincidence, but you're implementing something quite similar to "parser combinators", which is a topic of many academic papers :-). I think that Monadic Parser Combinators is quite readable (it has examples in Haskell, but you should be able to translate them to F#).
Regarding the function composition operator. I'm generally not a big fan of using the operator too much, because it often obfuscates the code. However, in your example it makes a good sense because you can easily imagine that
>>
means "this group should be followed by that group", which is easy to interpret.The only minor change that I would do is to choose some nice custom operator for the
disjunction
operation and define a few more primitive operations, so that you can write for example this:This is more higher-level description, so it removes some of the
>>
operators in favor of functions that are more descriptive (and encapsulate>>
). I also changedquantify
to take multiple arguments instead of a tripple (which is a minor change)If you want to play with this further, then you can take a look at the article and try to write F# computation expression builder that would allow you to use
parser { .. }
syntax.这通常是很好的风格,但你缺少一些技巧,并且仍然有相当多的冗余。也许更像是这样:
您可能更喜欢累积代表计算的值而不是闭包,因为它使调试更加容易。
This is generally good style but you're missing some tricks and still have quite a bit of redundancy. Maybe more like this:
You might prefer to accumulate a value representing a computation rather than closures because it makes debugging much easier.