单词的描述工具-正规式 :
多数程序设计语言的单词的语法均可用正规文法来表示。例:程序设计语言中几类单词的描述规则:标识符、无符号整数、运算符…。
正规式(regular expression也叫正则表达式):
正规式是定义正规集的数学工具,是说明单词的模式(pattern)的一种表示法,用它描述单词符号时一般比正规文法更简洁。
这里的RE(正则表达式)只有三个基本的操作:
(1)选择 取并集.符号:|. 比如两个字符串集合R和S的选择操作,记作R|S.
(2)连接 字符串之间的拼接.两个字符串集合R和S的连接为RS.
(3)闭包 符号:* 字符串集合R的闭包R*是指把R与自身连接零次或者多次形成的所有集合的并集.
由这几个简单的操作可以得到我们平常接触的正则表达式的所有扩展.
例 : 令∑={a,b},则∑上的正规式和相应正规集为
正规式 | 正规集 |
a | {a} |
a|b | {a,b} |
ab | {ab} |
(a|b)(a|b) | {aa,ab,ba,bb} |
a * | {ε ,a,aa, ……任意个a的串} |
(a|b)* | {ε ,a,aa, ……任意个a的串}{ε ,a,b,aa,ab,bb ……所有由 a和b组成的串} |
((a|b)* (aa|bb)(a|b)* | (aa|bb)(a|b)* ∑上所有含有两个相继的a或两个相继的b组成的串 |
例 : 令∑={a,d},其中a代表字母,d代表数字,则∑上的正规式 r=a(a|d) * 定义的正规集为
{a,aa,ad,add,……}, 即:字母(字母|数字) * ,它表示的正规集中的每个元素的模式是“字母打头的字母数字串”,也就是多数程序语言中标识符的词法规则.
如2017年上半年第21题:
在仅由字符a、b构成的所有字符串中,其中以b结尾的字符串集合可用正规式表示为()
a (b|ab)*b
b (ab*)*b
c a*b*b
d (a|b)*b
解答:若理解成,解释每个答案,看是否以b结尾,那就错了。其实真实题目的意思是,用正规式表示,仅ab构成的字符串表示或a或b构成的字符串,以b结尾。显然是选D
又如2016年下半年第48题
由字符a,b构成的字符串中,若每个a后至少跟一个b,则该字符串集合可用正规式表示为()
a (b|ab)*
b (ab*)*
c (a*b*)*
d (a|b)*
答:还是按照上面题,由ab构成的字符串,由此只能是选a
有限状态自动机(Finite Automaton,FA)
我说的时候喜欢加上状态两个字,因为FA的关键动作就是状态间的转移.FA有一个状态集S,对于每一个输入都会让FA的状态进行转移.如果能够从起始状态转移到接受状态,那么输入序列就被识别了.不存在空字符串ε的状态转移.
非确定性有限状态自动机(Non-deterministic Finite Automaton,NFA).对于同一输入转移到多个不同的状态或者存在空字符串ε的状态转移的FA.
确定性有限状态自动机(Deterministic Finite Automaton,DFA).对于任何确定的输入都只有唯一确定的转移且不存在空字符串ε的状态转移的FA.
NFA到DFA 是对NFA的简化过程.
NFA到DFA的子集构造算法(The Subset Construction):从将初始状态划分为一个初始状态子集开始,构造状态子集(经过零个或多个空字符串ε转移到的状态和已在子集中的状态都是构造的新的状态子集),存在c属于字母表Σ,经过一个c的转移(必须有c的转移),能够使得从状态子集ni转移到状态子集nj,则在DFA中有在c的输入下从状态子集ni转移到状态子集nj的转移.最后不再有新的状态子集出现.根据状态子集的转移依次构造DFA.
NFA与DFA
对比于NFA与DFA,其区别仅在于初态S0,确定的有限状态自动机的S0是唯一确定的。而不确定的有限状态自动机的初态是一个集合,即有多个初态。
由于NFA是一种状态不确定的自动机,所以这种自动机不便机器实现;DFA是有限确定状态的自动机,它的状态转换的条件都很确定,所以它比较方便机器实现,同时在识别能力也和NFA相当(相关的书中已经证明了每一种NFA都可转换为同样识别能力的DFA),所以转换为DFA是更有利于机器实现。
ps:对于正则表达式而言,每一个表达式都可以等价于一个NFA,其转化过程如图:
因为 NFA 的状态转移不确定,不适合直接做词法分析器的识别,在写算法时往往需要使用回溯。所以我们一般使用子集构造算法,将 NFA 转换成 DFA, 得到确定的状态转移,再转化成一个词法分析器的代码。
下面给出一个关于 NFA 到 DFA 转化的例子,我们使用 a(b|c)* 做例:
对于 ε 的边表示一种零代价的转换,n1 可以在没有任何输入操作的情况下直接滑动到 n2,也就是 n1 和 n2 是等价的。
所以 n0 通过 a 可以走到 n1, n2, n3, n4, n6, n9。我们可以将这样的 6 个元素记为一个集合 q1。 q1 = {n1, n2, n3, n4, n6, n9} 。
q1 通过 b 可以得到:n5, n8, n9, n3, n4, n6 ,记为 q2。
q2 继续通过某一节点得到 q3,
继续重复该步骤,得到所有的子集。
所以 q0 通过 a 得到 q1, q1 通过 b 得到 q2 .... ,最终可以将 NFA 转化为一个 DFA。
因为 q1 和 q2 中都包含 n9, 所以 q1 和 q2 都是接受状态。
对于子集的求解,首先我们先看出在 NFA 中的下一状态,如 q1 的下一状态记为 delta(q1), 在这里是 {n5} 。之后求它的边界, 即每一个元素都通过 ε 走到能走到的所有状态,记为 q1 的闭包。
上下文无关文法是描述程序语言语法的强有力的数学工具
乔姆斯基文法体系
3型文法:正则文法:词法结构
2型文法:上下文无关文法:语法结构
1型文法:上下文有关文法
0型文法:任意文法
每一个外部文法(大圈)都比内部文法(小圈)表达能力强
举个自然语言处理的例子
- 自然语言中的句子的典型结构
- 主语 谓语 宾语
- 名词 动词 名词
- 例子:
- 名词:{羊, 老虎, 草, 水}
- 动词:{吃, 喝}
- 句子:
- 羊 吃 草
- 羊 喝 水
- 老虎 吃 老虎
- 草 吃 老虎
- …
对这个例子,我们进行形式化分析:
(S表示句子,->表示推出,N表示名词,V表示动词)
S -> N V N
N -> s(sheep) | t(tiger) | g(grass) | w(water)
V -> e(eat) | d(drink)
我们将其中的大写符号叫做非终结符:{S, N, V}
将小写的符号(名词+谓词)叫做终结符:{s, t, g, w, e, d}
开始符号是:S