从正则表达式创建NFA时,我遇到了“描述每个步骤”的问题。问题如下:
将以下正则表达式转换为非确定性有限状态自动机(NFA),清楚地描述您使用的算法的步骤: (B | A)* B(A | B)
我制作了一个简单的三态机器,但它非常直观。 这是我的讲师在过去的考试中提出的一个问题,他也写了以下Thompson算法的解释:http://www.cs.may.ie/staff/jpower/Courses/Previous/parsing/node5.html
任何人都可以清楚地“清楚地描述每一步”吗?它看起来像是一组基本规则,而不是一个步骤可以遵循的算法。
也许我已经在某个地方掩饰了一种算法,但到目前为止,我只是用一种有根据的猜测来创建它们。
答案 0 :(得分:62)
一般方法的简短版本。
有一个算法称为Thompson-McNaughton-Yamada构造算法,有时只是“Thompson Construction”。一个构建中间NFA,沿着路径填充碎片,同时尊重运算符优先级:第一个括号,然后是Kleene Star(例如,a *),然后是连接(例如,ab),然后是交替(例如,a | b)。
以下是构建(b | a)* b(a | b)的NFA
的深入演练构建顶级
处理括号。注意:在实际实现中,通过对其内容的递归调用来处理括号是有意义的。为了清楚起见,我会推迟对parens内部任何内容的评估。
Kleene Stars:只有一个*,所以我们建立一个名为P的占位符Kleene Star机器(后来将包含b | a)。
中间结果:
连接:将P附加到b,并将b附加到名为Q的占位符机器(将包含(a | b)。中间结果:
括号外没有替换,所以我们跳过它。
现在我们坐在P * bQ机器上。 (请注意,我们的占位符P和Q只是连接机器。)我们用BFA替换P边缘为b | a,并通过递归应用上述步骤将Q边缘替换为NFA用于| b。
构建P
跳过。没有parens。
跳过。没有Kleene明星。
跳过。没有连接。
为b | a构建交替机器。中间结果:
整合P
接下来,我们回到那台P * bQ机器,我们撕掉了P边缘。我们将P边缘的源用作P机器的起始状态,并且P边缘的目的地用作P机器的目标状态。我们也使该州拒绝(剥夺其作为接受国的财产)。结果如下:
构建Q
跳过。没有parens。
跳过。没有Kleene明星。
跳过。没有连接。
为a | b构建交替机器。顺便说一下,交替是可交换的,因此a | b在逻辑上等价于b | a。 (阅读:从懒惰中跳过这个小脚注图。)
整合Q
除了用我们构建的机器中间体b替换Q边缘之外,我们用上面的P做了。结果如下:
多田!呃,我的意思是,QED。
想知道更多?
以上所有图片均使用an online tool for automatically converting regular expressions to non-deterministic finite automata生成。您可以在线找到source code for the Thompson-McNaughton-Yamada Construction algorithm。
该算法也在Aho's Compilers: Principles, Techniques, and Tools中得到解决,尽管其解释在实现细节上很少。你也可以向优秀的Russ Cox学习an implementation of the Thompson Construction in C,他在一篇关于regular expression matching的热门文章中详细描述了它。
答案 1 :(得分:1)
在下面的GitHub存储库中,您可以找到Thompson构造的Java实现,首先从正则表达式创建NFA,然后输入字符串与该NFA匹配:
答案 2 :(得分:0)
https://github.com/White-White/RegSwift
没有多余的单词。查看此存储库,它将您的正则表达式转换为NFA,并直观地显示NFA的状态转换。