NFA/DFA算法 (2)

第四,一个规则可以是可选的。 可选的规则实 际上是属于并联的一种特殊形式。加入我们需要规则”abc”和”abcde”并联,我们会发现这两个规则有着相同的前缀”abc”,而且这个前缀恰好就是 其中的一个规则。于是我们可以把规则改写成”abc”与””和”de”的并联的串联。但是规则””指定的规则是空串,因此这个规则与”de”的并联就可以 看成是一个可选的规则”de”。

第五,规则可以被重复。 有限次的重复可以使 用串联表示,但是如果我们不想限制重复的次数的话,串联就没法表示这个规则了,于是我们引入了“重复”。一个典型的例子就是程序设计语言的标识符。标识符 可以是一个变量的名字或者是其他东西。一门语言通常没有规定变量名的最大长度。因此为了表示这个规则,就需要将52个字母进行并联,然后对这个规则进行重 复。

上述的5种构造规则的方法中,后面的4个方法被用于把规则组合成为更大的规则。为了给出这种规则的形式化表示,我们引入了一种范式。这种范式有以下语法:

1:字符用双引号包围起来,空串使用ε代替。

2:两个规则头尾连接代表规则的串联。

3:两个规则使用 | 隔开代表规则的并联。

4:规则使用[]包围代表该规则是可选的,规则使用{}包围代表该规则是重复的。

5:规则使用()包围代表该规则是一个整体,通常用于改变操作符 | 的优先级。

举个例子,一个实数的规则书写如下:

{“0”|”1”|”2”|”3”|”4”|”5”|”6”|”7”|”8”|”9”}”.”[{“0”|”1”|”2”|”3”|”4”|”5”|”6”|”7”|”8”|”9”}]。

但是,我们如何表示“不是数字的其他字符呢”?字符的数量是有限的,因此我们可 以使用规则的并联来表示。但是所有的字符实在是太多(ASCII字符集有127个字符,UTF-16字符集有65535个字符),因此后来人们想出了各种 各样的简化规则书写的办法。比较著名的有BNF范式。BNF范式经常被用于理论研究,但是更加实用的是正则表达式。

正则表达式的字符不需要用双引号括起来,但是如果需要表示一些被定义了的字符 (如 “|” )的话,就使用转义字符的方法表示(如 “/|”)。其次,X?代表[X],X+代表{X},X*代表[{X}]。字符集合可以用区间来表示,[0-9]可以表示 “0”|”1”|”2”|”3”|”4”|”5”|”6”|”7”|”8”|”9”,[^0-9]则表示“除了数字以外的其他字符”。正则表达式还有各种 各样的其他规则来简化我们的书写,不过由于本文并不是“精通正则表达式”,因此我们只保留若干比较本质的操作来进行词法分析原理的描述。

正则表达式的表达能力极强,小数的规则可以使用[0-9]+.[0-9]来表示,C语言的注释可以表示为//*([^/*]|/*+[^/*/])*/*+/来表示。

3、有穷状态自动机

人阅读正则表达式会比较简单,但是机器阅读正则表达式就是一件非常困难的事情 了。而且,直接使用正则表达式进行匹配配的话,不仅工作量大,而且速度缓慢。因此我们还需要另外一种专门为机器设计的表达方式。本文在以后的章节中会给出 一种算法把正则表达式转换为机器可以阅读的形式,就是这一章节所描述的有穷状态自动机。

有穷状态自动机这个名字听起来比较可怕,不过实际上这种自动机并没有想象中的那 么复杂。状态机的这种概念被广泛的应用在各种各样的领域中。软件工程的统一建模语言(UML)有状态图,数字逻辑中也有状态转移图。不过这些各种各样的图 在本质上都跟状态机没有什么区别。我将会通过一个例子来讲述状态的实际意义。

假设我们现在需要检查一个字符串中a的数量和b的数量是否都是偶数。当然我们可 以用一个正则表达式来描述它。不过对于这个问题来说,用正则表达式来描述远远不如构造状态机方便。我们可以设计出一个状态的集合,然后指定集合中的某一个 元素为“起始状态”。其实状态就是在工作还没开始的时候,分析器所处的状态。分析器在每一次进行一项新的工作的时候,都要把状态重置为起始状态。分析器每 读入一个字符就修改一次状态,修改的方法我们也可以指定。分析器在读完所有的字符以后,必然停留在一个确定的状态中。如果这个状态跟我们所期望的状态一致 的话,我们就说这个分析器接受了这个字符串,否则我们就说这个分析器拒绝了这个字符串。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzppxj.html