【学习笔记】自动机理论、语言和计算导论(三)

正则表达式

表达式运算符(优先级递增顺序)

  1. 两个语言的并 L ∪ M L\cup M LM:只属于 L L L或只属于 M M M,或者同时属于二者的串的集合。
  2. 两个语言的连接 L M LM LM:取 L L L中任意一个串,与M中任意一个串连接起来的串集合。
  3. 语言的闭包 L ∗ L^* L L L L中任意多串的连接的并。 ⋃ i ≥ 0 L i \bigcup_{i\ge 0}L^i i0Li ∅ ∗ = { ε } \varnothing^*=\{\varepsilon\} ={ε} ∅ 0 = { ε } \varnothing^0=\{\varepsilon\} 0={ε}

构造正则表达式
对每个正则表达式 E E E,描述了 E E E所代表的语言 L ( E ) L(E) L(E)
基础:

  1. 常量 ε \varepsilon ε ∅ \varnothing 是正则表达式,分别表示语言 { ε } \{\varepsilon\} {ε} ∅ \varnothing 。也就是说, L ( ε ) = { ε } L(\varepsilon)=\{\varepsilon\} L(ε)={ε} L ( ∅ ) = ∅ L(\varnothing)=\varnothing L()=
  2. a a a是任意符号,则 a \mathbf{a} a是正则表达式。这个表达式表示语言 { a } \{a\} {a}。也就是说, L ( a ) = { a } L(\mathbf{a})=\{a\} L(a)={a}。注意,用黑体字表示一个符号所对应的表达式。这种对应关系应当是明显的,如 a \mathbf{a} a指的是 a a a
  3. 变量,通常用大写斜体符号表示,如 L L L。它代表任意语言。

归纳: 三种运算符,括号的引入。

  1. 如果 E E E F F F都是正则表达式,则 E + F E+F E+F是正则表达式,表示 L ( E ) L(E) L(E) L ( F ) L(F) L(F)的并。 L ( E + F ) = L ( E ) ∪ L ( F ) L(E+F)=L(E)\cup L(F) L(E+F)=L(E)L(F)
  2. 如果 E E E F F F都是正则表达式,则 E F EF EF是正则表达式,表示 L ( E ) L(E) L(E) L ( F ) L(F) L(F)的连接。也就是说, L ( E F ) = L ( E ) L ( F ) L(EF)=L(E)L(F) L(EF)=L(E)L(F)。注意,可以任意地用点来表示连接运算符,既作为语言上的运算,也作为正则表达式上的运算符。例如, 0.1 \mathbf{0.1} 0.1是正则表达式,与 01 \mathbf{01} 01的意思一样,表示语言 { 01 } \{01\} {01}。但是在正则表达式中避免用点作为连接。
  3. 如果 E E E是正则表达式,则 E ∗ E^* E是正则表达式,表示 L ( E ) L(E) L(E)的闭包。 L ( E ∗ ) = ( L ( E ) ) ∗ L(E^*)=(L(E))^* L(E)=(L(E))
  4. 如果 E E E是正则表达式,则 ( E ) (E) (E)也是正则表达式,与 E E E表示相同的语言。 L ( ( E ) ) = L ( E ) L((E))=L(E) L((E))=L(E)

例:一个正则表达式,表示交替的0和1的串的集合:
( 01 ) ∗ + ( 10 ) ∗ + 0 ( 10 ) ∗ + 1 ( 01 ) ∗ 或 ( ε + 1 ) ( 01 ) ∗ ( ε + 0 ) \mathbf{(01)^{*}+(10)^{*}+0(10)^{*}+1(01)^{*} \qquad 或 \qquad ( \varepsilon + 1 ) ( 01 ) ^ { * } ( \varepsilon + 0 )} (01)+(10)+0(10)+1(01)(ε+1)(01)(ε+0)

有穷自动机和正则表达式

有穷自动机和正则表达式所描述的语言是等价的。

定理:如果对于某个DFA A A A L = L ( A ) L=L(A) L=L(A),则存在一个正则表达式 R R R,使得 L = L ( R ) L=L(R) L=L(R)
证明:
设对于某个整数 n n n A A A的状态是 { 1 , 2 , ⋯   , n } \{1,2,\cdots,n\} {1,2,,n}。无论 A A A的状态实际是什么,对于某个有穷的 n n n,都会有 n n n个状态,通过为这些状态改名,可以以前 n n n个正整数方式来引用这些状态。下面构造一组正则表达式,来描述 A A A的转移图中越来越大的路径集合。
R i j ( k ) R_{ij}^{(k)} Rij(k)作为正则表达式的名字,这些表达式的语言是下列串 w w w的集合:使得 w w w A A A中从状态 i i i到状态 j j j的路径的标记,而且这条路径没有编号大于 k k k的中间顶点。注意,路径的起点和终点都不是“中间的”,所以不限制 i i i和(或) j j j要小于或等于 k k k
为了构造表达式 R i j ( k ) R_{ij}^{(k)} Rij(k),使用下面的归纳定义,从 k = 0 k = 0 k=0开始,最终到达 k = n k = n k=n。注意,当 k = n k = n k=n时,在所表示的路径上根本没有限制,因为没有状态比 n n n还大。
基础:基础是 k = 0 k = 0 k=0。由于所有状态都编号为1或更大,路径上的限制是:路径必定根本没有中间状态。只有两种路径满足这样的条件:
1.从顶点(状态) i i i到顶点 j j j的一条箭弧。
2.只包含某个顶点 i i i的长度为0的路径。
如果 i ≠ j i\neq j i=j,则只有情形(1)是可能的。我们必须检查这个DFA A A A,并寻找这些输入符号 a a a:使得在符号 a a a上有从状态 i i i到状态 j j j的转移。
a) 如果没有这样的符号 a a a,则 R i j ( 0 ) = ∅ R_{ij}^{(0)}=\varnothing Rij(0)=
b) 如果恰好有一个这样的符号 a a a,则 R i j ( 0 ) = a R_{ij}^{(0)}=\mathbf{a} Rij(0)=a
c) 如果有符号 a 1 , a 2 , ⋯   , a k a_1,a_2,\cdots,a_k a1,a2,,ak,都标记从状态 i i i到状态 j j j的箭弧,则 R i j ( 0 ) = a 1 + a 2 + ⋯ + a k R_{ij}^{(0)}=\mathbf{a_1+a_2+\cdots+a_k} Rij(0)=a1+a2++ak
但是,如果 i = j i = j i=j,则合法路径就是长度为0的路径和所有从 i i i到自身的环。长度为0的路径表示成正则表达式 ε \varepsilon ε,因为这个路径沿途没有符号。因此,把 ε \varepsilon ε加入上面(a)到©所设计的各种表达式中。也就是说,在情形(a)下(没有符号a),表达式成为 ε \varepsilon ε;在情形(b)下(一个符号a),表达式成为 ε + a \varepsilon+\mathbf{a} εa;在情形©下(多个符号),表达式成为 R i j ( 0 ) = a 1 + a 2 + ⋯ + a k R_{ij}^{(0)}=\mathbf{a_1+a_2+\cdots+a_k} Rij(0)=a1+a2++ak
归纳∶假设存在从 i i i j j j的路径不经过比 k k k高的状态。有两种可能的情形需要考虑:
1.这条路径根本不经过状态 k k k。在这种情形下,路径的标记属于 R i j ( k − 1 ) R_{ij}^{(k-1)} Rij(k1)的语言。
2.这条路径经过状态 k k k至少一次。于是把路径分成几段,如图3-3所示。第一段不经过 k k k而从状态 i i i到状态 k k k,最后一段不经过 k k k而从 k k k j j j,所有中间路段都不经过 k k k而从 k k k到自身。注意,如果路径只经过状态 k k k一次,则没有"中间"段,只有从 i i i k k k的路径和从 k k k j j j的路径。所有这种路径的标记的集合表示成正则表达式 R i k ( k − 1 ) ( R k k ( k − 1 ) ) ∗ R k j ( k − 1 ) R_{ik}^{(k-1)}(R_{kk}^{(k-1)})^*R_{kj}^{(k-1)} Rik(k1)(Rkk(k1))Rkj(k1)。也就是说,第一个表达式表示第一次到达状态 k k k的路径部分,第二个则表示从 k k k到自身零次、一次或多次的部分,第三个表达式表示最后一次离开 k k k并到达状态 j j j的路径部分。
3-3
把上面两种路径的表达式组合起来,得到表达式 R i j ( k ) = R i j ( k − 1 ) + R i k ( k − 1 ) ( R k k ( k − 1 ) ) ∗ R k j ( k − 1 ) R_{ij}^{(k)}=R_{ij}^{(k-1)}+R_{ik}^{(k-1)}(R_{kk}^{(k-1)})^*R_{kj}^{(k-1)} Rij(k)=Rij(k1)+Rik(k1)(Rkk(k1))Rkj(k1)
表示从状态 i i i到状态 j j j而不经过比 k k k更高状态的所有路径的标记。如果按照上标递增的顺序来构造这些表达式,则由干每个 R i j ( k ) R_{ij}^{(k)} Rij(k)只依赖于上标更小的表达式,所有的表达式都在需要时已经构造出来了。
最终对于所有 i i i j j j,都得到 R i j ( n ) R_{ij}^{(n)} Rij(n)。可以假设,状态1是初始状态,而接受状态可以是任意一组状态。自动机的语言的正则表达式,就是所有表达式 R 1 j ( n ) R_{1j}^{(n)} R1j(n)之和(并),使得状态 j j j是接受状态。

正则表达式的应用

对于搜索文本中模式的应用来说,正则表达式是一种选择媒介,给出了要识别模式的"图像"。然后在后台正则表达式被编译成确定型自动机或非确定型自动机,再通过模拟自动机来产生识别文本中模式的程序。

UNIX中的正则表达式
(扩展记号)引入字符类,紧凑地表示大的字符集

  • 符号. (点)表示“任意字符”。
  • 序列 [ a 1 a 2 ⋯ a k ] [a_1a_2\cdots a_k] [a1a2ak]表示 a 1 + a 2 + ⋯ a k a_1+a_2+\cdots a_k a1+a2+ak
  • 在方括号之间规定形如 x − y x-y xy的范围,表示ASCII序列中从 x x x y y y的所有字符。例如,数字表示成[0-9],大写字母表示成[A-Z],所有字母和数字的集合表示成[A-Za-z0-9]。如果要在字符列表中包含负号,就放在开头或结尾,这样不会与字母范围的形式相混淆。例如,要形成带符号的十进制数,所用的数字集合以及点、加号和负号等表示成[-+.0-9]。方括号或者在UNIX正则表达式中有特殊意义的其他字符,表示成在对应字符前加一个斜杠(\)。
  • 几种最常见的字符类有特殊记号。例如∶
    a)[:digit:]是十进制数字集合,与[0-9]相同。
    b)[:alpha:]表示任何字母字符,与[A-Za-z]相同。
    c)[:alnum:]表示数字和字母(字母和数字字符),与[A-Za-z0-9]相同。

另外,有几个在UNIX正则表达式中使用的运算符,不扩大所表示的语言范围,但有时更容易表达所要表达的东西。

  • ∣ | 代替 + + +来表示并。
  • 运算 ? ? ? 表示“0个或1个”。因此,UNIX中 R ? R? R?与本书中 ε + R \varepsilon+R εR一样。
  • 运算 + + +表示“1个或多个”。因此,UNIX中 R + R+ R+与本书中 R R ∗ RR^* RR一样。
  • 运算 { n } \{n\} {n}表示"n个副本"。因此,UNIX中 R { 5 } R\{5\} R{5} R R R R R RRRRR RRRRR的缩写。
  • UNIX正则表达式允许用括号来对子表达式分组,并且采用同样的运算符优先级(考虑优先级时,?、+和 { n } \{n\} {n}按*对待)。UNIX中使用星运算符*与本书前面所用的意思相同。

词法分析
称为"词法分析器"的编译器部件扫描源程序,识别所有的记号(token),即在逻辑上成为一体的连续字符的子串。关键字和标识符都是记号的常见例子,但还有许多其他例子。
UNIX命令lex和GNU版本的flex都接受UNIX风格的正则表达式列表作为输入,每个正则表达式后面跟着花括号内的一节代码,当词法分析器发现记号实例时,代码指示词法分析器如何工作。这样的工具称为词法分析器生成器,因为把词法分析器的高层描述作为输入,由此产生正确的词法分析器函数。
已经发现,像lexflex这样的命令非常有用,因为正则表达式记号恰好具备了描述记号所需要的能力。这些命令能够利用从正则表达式到自动机的转换过程来生成有效的函数,把源程序分解成记号。这使得实现一个词法分析器只要半天工夫,而在开发这些基于正则表达式的工具之前,手工生成词法分析器要花费数月时间。而且,如果出于任何理由需要修改词法分析器,只需要修改正则表达式而不是代码。

查找文本中的模式
例子:为街道地址开发的表达式(UNIX风格)
'[0-9]+[A-Z]?[A-Z][a-z]*([A-Z][a-z]*)*(Street|St\.|Avenue|Ave\.|Road|Rd\.)'

正则表达式代数定律

并的幂等律: L + L = L L+L=L L+L=L
并的交换律: L + M = M + L L+M=M+L L+M=M+L
并的结合律: ( L + M ) + N = L + ( M + N ) (L+M)+N=L+(M+N) (L+M)+N=L+(M+N)
连接的结合律: ( L M ) N = L ( M N ) (LM)N=L(MN) (LM)N=L(MN)(连接没有交换律)
∅ \varnothing 是并运算的单位元: ∅ + L = L + ∅ = L \varnothing+L=L+\varnothing=L +L=L+=L
ε \varepsilon ε是连接运算的单位元: ε L = L ε = L \varepsilon L=L\varepsilon=L εL=Lε=L
∅ \varnothing 是连接运算的零元: ∅ L = L ∅ = ∅ \varnothing L=L\varnothing=\varnothing L=L=
连接对于并的左分配律: L ( M + N ) = L M + L N L(M+N)=LM+LN L(M+N)=LM+LN
连接对于并的右分配律: ( M + N ) L = M L + N L (M+N)L=ML+NL (M+N)L=ML+NL
闭包的定律: ( L ∗ ) ∗ = L ∗ (L^*)^*=L^* (L)=L ∅ ∗ = ε \varnothing^*=\varepsilon =ε ε ∗ = ε \varepsilon^*=\varepsilon ε=ε L + = L + L L + L L L + ⋯ = L L ∗ = L ∗ L L^+=L+LL+LLL+\dots=LL^*=L^*L L+=L+LL+LLL+=LL=LL L ∗ = L + + ε L^*=L^++\varepsilon L=L++ε L ? = ε + L L?=\varepsilon+L L?=ε+L

定理(发现正则表达式定律) 设 E E E是带变量 L 1 , L 2 … , L m L_1, L_2\dots,L_m L1,L2,Lm的正则表达式。对于 i = 1 , 2 , … , m i=1,2,\dots,m i=1,2,,m,通过把 L i L_i Li的每次出现都换成符号 a i a_i ai形成具体的正则表达式 C C C。于是对于任意的语言 L 1 , L 2 … , L m L_1, L_2\dots,L_m L1,L2,Lm,每一个属于 L ( E ) L(E) L(E)的串 w w w都可写成 w = w 1 w 2 … w k w=w_1w_2\dots w_k w=w1w2wk,其中每个 w i w_i wi都属于任意的语言之一(如 L j i L_{j_i} Lji),而且串 a j 1 a j 2 … a j k a_{j_1}a_{j_2}\dots a_{j_k} aj1aj2ajk,属于语言 L ( C ) L(C) L(C)。非形式化地说,从每个属于 L ( C ) L(C) L(C)的串开始(如 a j 1 a j 2 … a j k a_{j_1}a_{j_2}\dots a_{j_k} aj1aj2ajk),把每个 a j i a_{j_i} aji,都换成对应语言 L j i L_{j_i} Lji中的任意串,这样就构造出了 L ( E ) L(E) L(E)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值