第三部分 上下文无关语言和下推自动机
前面介绍的有限自动机是计算的初级模型,它所接受的正规语言不太关心字符串自身的结构。上下文无关文法(CFL)是一种简单的描述语法规则的递归方法,语言中的字符串由这些规则产生。所有的正规语言都能用上下文无关文法描述,它也可以描述非正规语言。上下文无关文法描述的语法规则更复杂多变,可以在相当大的程度上,描述高级程序设计语言的语法和其他一些形式语言。
类似正则语言对应的抽象机模型是有限自动机,CFL也有对应的抽象机模型。CFL对应的计算模型是在有限自动机的基础上增加存储空间得到,并被设想成无限空间(对应有限自动机的有限空间),采用了一种简单的管理模式,栈(stack),这种新的计算模型(或抽象机)称为下推自动机(pushdown automata),下推是栈最典型的操作。有必要在下推自动机中保留非确定性,确定型下推自动机不能接受所有的CFL,但给定一个CFG,容易构造一个相应的非确定型下推自动机,它在识别字符串过程中的移动模拟了文法的推导过程,这个过程称为分析(parse)。分析不是一定需要下推自动机来完成。
CFL仍然不够通用,不能包括所有有意义的、或有用的形式语言。采用类似第五章的技术,我
们将给出一些不是CFL的简单例子,这些技术也用于解决与CFL相关的判定问题。


6 上下文无关文法
6.1 上下文无关文法的定义
为了描述我们在第二部分考察的各种语言,包括一些非正则语言,我们引入一种语言的递归定义方法,称为文法。文法与我们熟悉的语言的语法描述相近,是描述语言和分析语言的有力工具。
问题:文法的形式化定义似乎可以模仿有限自动机,比如5元组或6元组之类。
例子6.1 正如我们在例子2.16中所见,字母表{a, b}上的回文语言pal可以用下面的递归方法描述:
1. , a, b pal
2. 对每个S pal,aSa和bSb也属于pal
3. pal中不包含其他字符串
如果将上面的符号S看成一个变量,代表了所有我们希望计算(比如某种递归算法)的pal的元素,那么上面的规则1和规则2可以非正式地重新表述如下:
1. S的值可以是 , a, b
2. 每个S可以写成aSa或bSb的形式
如果我们用 表示“可以取值为”,则可以写出下面的式子:
    S aSa abSba ab ba=abba
    上面的产生过程可以总结成下面的两组产生式(或称规则):
    S a | b |
    S aSa | bSb
    符号“|”表示“或”的含义。上式的含义是aSa或bSb,而不是a或b,即连接运算的优先级高于“|”。我们使用的这套术语中,小写字母a和b表示终结符,大写字母S表示非终结符,或称变量。总共有5条规则,或产生式(production)。符号S是非终结符,也是起始终结符,即我们生成字符串的起始符号是S,然后不断利用规则替换符号串中的非终结符,直到最终得到一个不含非终结符的符号串,就生成了规则所定义的语言的一个字符串。
    例子2中的产生式具有除起始符S外的多个非终结符,我们设想S表示了语言中任意的字符串,其他非终结符表示了其他辅助性的字符串类型,他们可用来方便地生成S表示的字符串。
    例子6.2 我们要构造一个生成所有在字母表{a, b}上的非回文字符串的文法,那样的字符串可以描述如下:从字符串的两端开始比较,也许能够发现一些相同的字符对,但最终能够发现一对不同的字符。对于前一种情况,我们可以借用回文语言的产生式:
    S aSa | bSb
如果加入产生式
    S a | b |
则这种左右匹配的形式将体现在整个字符串上,为了中断这种左右匹配的情况,即体现上面提到的第二种情况,我们引入新的非终结符,比如D,表示那些左右两个端点上的字符不同的字符串。且所有符合D的字符串也符合S,因此有S D。
    非终结符的定义比较简单,它唯一的条件是左右两个端点的字符不相同,中间的字符串可以是任意的,我们用非终结符A表示任意的字符串,则有D aAb | bAa。
    A表示任意的字符串,因此A的产生式更简单了,不用添加新的非终结符来简化问题,它的产生式是,A  | aA | bA。
    我们把上面三个非终结符的产生式写在一起,就得到了描述所规定语言的产生式集:
    S aSa | bSb | D
    D aAb | bAa
    A  | aA | bA
    因此一个完整的非回文字符串“abbaaba”的产生过程是,
    S aSa abSba abDba abbAaba abbaAaba abba aba abbaaba
    定义6.1 上下文无关文法(context-free grammar, CFG)是一个4元组G=(V, , S, P),其中,V和 是不相交的有限集,S V,P是一组有限的产生式规则,形如A  ,其中A V,且  (V  )*。
V的元素称为非终结符(或变量), 的元素称为终结符,S是一个特殊的非终结符,称为起始符,P的元素称为语法规则,或产生式。
设G=(V, , S, P)是一个CFG,我们将符号 保留给P的产生式专用,符号 G用于表示字符串的产生过程的每一步,如  G 表示字符串 能够通过替换 中的某些变量(根据P定义的产生式来替换)得到,即如果有
= 1A 2且A  P,则 = 1  2
这里能够看到,我们命名为上下文无关(context-free)的含义,即我们在利用产生式规则,
用一组符号替换某个非终结符时,与非终结符的上下文无关(此处,A的替换与 1 2无关),替换是无限制的。
符合标识符的字符串是什么    在没有多个文法出现,很清楚用到文法G是什么的情况下,推导符号 G可以简写成 。多步的推导可以写成 *G,即如果存在一组符号串, = 0、 1、…、 k= ,每个后者都是前者的直接推导,则称为 可以多步推导出 ,记为  *G ,简写成  *
    定义6.2 G=(V, , S, P)是一个CFG,则G产生的语言是所有可由G产生的字符串组成的集合,即L(G)={x  * | S *Gx}。一个语言L是上下文无关语言(context-free language, CFL),当且仅当存在一个CFG G,使得L=L(G)。
   
此处可以把文法设想成类似自动机的抽象模型,则一个语言L是CFL当且仅当存在一个CFG G接受它(或识别它),类似前面正则语言与有限自动机的关系,接受(或识别)的含义是两方面的,一方面凡是L中的字符串都能由G产生,另一方面,凡是不属于L的字符串都不能由G产生。前面的例子,能够比较容易地说明这两方面的限制,下面的例子则不是很明显。
例子6.3 考虑语言L={x {0, 1}* | n0(x)=n1(x)},其中ni(x)表示数字i在x中的出现次数(即含有相同数目0和1的语言)。写出生成L的CFG。
分析:既然CFG的本质是一个递归定义,那么类似例子6.1,我们可以先发现归纳基础,然后到归纳推理。显然  L,另外如果存在一个字符串x L,那么得到更长的属于L的字符串的两个方法是,0x1和1x0,即分别在两端添加相同数量的0和1。(当然,还有很多生成方法,比如x01,x10,或在x中插入相同数量的0和1),这样得到三个产生式:
S  | 0S1 | 1S0
显然,还遗漏了一些字符串,如0110。我们注意到L中任意两个元素的连接仍然属于L,因此可以增加一个产生式,S SS。与前面的三个产生式合并,我们得到一个CFG G如下,
S  | 0S1 | 1S0 | SS
显然G产生的字符串都满足0和1数目相等这个条件,即L(G) L,现在只要证明L L(G)。
令d(x)=n0(x)-n1(x)。则字符串x L,当且仅当d(x)=0。因此只需证明每个满足d(x)=0的x,都有x L(G)。我们用数学归纳法来证明L L(G)。归纳对象是字符串的长度|x|。
1. 归纳基础,|x|=0且d(x)=0,则x L(G),这是显然的,因为此时x= ,而 可以由产生式S  得到。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。