形式语言与编译 九 CFG到CNF再到GNF 左递归消除

Posted fennleo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了形式语言与编译 九 CFG到CNF再到GNF 左递归消除相关的知识,希望对你有一定的参考价值。

技术图片

先找出(N_A簇,N_B簇,N_C簇),先对(N_A簇):是单产生式的 将来会因为替换而消除;不是单产生式的 直接放进新的集合

同理对(N_B簇,N_C簇)也是一样(这样一般得到三个"堆")

技术图片

确实没了单产生式。

CFG的化简

建议做的过程:

  1. 消除(epsilon) 产生式
  2. 消除单产生式
  3. 消除无用符号

按这个顺序做

这样化简后的CFG G1 与原来的CFG G相比,有(L(G_1)=L(G)-{epsilon})

对上面简化后的CFG进行标准化——Chomsky范式

本来我们的CFG文法它的形式是 (A->alpha,其中alphain(V_N cup V_T)^*) 也就是这个串有终结符和非终结符组成的串即可,要求并不严

现在我们进行乔姆斯基范式的正规化。目的就是让我们上面的定义受到一些约束,形成这种(A->BC,A->a ,ain V_T) 类型((A->变元变元,A->终结符))的。

(这里与我们的正则文法,可以类比,正则文法的形式是:(A->alpha , A->alpha B,alphain (V_T)^*),也就是终结符组成的字符串 或者 终结符组成的字符串+变量)

注意:若语言(epsilon in L(G)) 就会有(S->epsilon)这样一条产生式加入产生式集合 这里很好理解,但是要求(S)只能当做开始符号,不能在到别的产生式的产生式体中,要是迫不得已,则就是我们引进$S_1->S与S_1->epsilon $

对每一个CFG 都有乔姆斯基范式CNF,也就是都可以化成(A->BC,A->a ,ain V_T) 这种形式

从上下文无关文发(CFG)到CNF(乔姆斯基范式)的算法

技术图片

说明:

扫描原来产生式,如果是终结符,就把这个终结符拎出来,形成产生式 【新变量->终结符】 的形式,再把原来产生式中的终结符替换成新变量

如果是非终结符(变量),把这个长的变量组成的串 想办法缩短成多个小短串(小短串的产生式体只包含不超过2个变量)

技术图片

Greibach范式(GNF)

为什么要提出Greibach范式???

因为自上而下的推导中会产生回溯,回溯导致分析效率降低

产生回溯的原因:

  • 存在(A->Ab) 左递归的非终结符
  • 同一个非终结符的候选式们 有公共左因子 (产生体左边有一部分一样的)

如何避免回溯?? 消除左递归!

Greibach范式的定义 :

把所有产生式定义成:(A->aeta,Ain V_N ,ain V_T,etain (V_N)^*) 且G不含(epsilon) 产生式 模式:变量——>常量+纯变量串 和CNF范式一样的是:每一个CFG也都有对应的GNF

技术图片

这里的多步推导 隐含意思(直接+间接)就是文法中几乎可以说 不能含有(A=>^*Aeta,A=>^*alpha A eta, …) 这种文法。一旦含有则直接被判定为递归文法

步骤:

  • 生成式的替换(替换的目的是为 后面找到间接左递归做准备)

技术图片

技术图片

技术图片

(截止这里,还没有正真的进行消除左递归,只是将所有可能隐含的左递归,通过替换算法给"发掘"出来)

  • 消除直接左递归

技术图片

先把不是左递归的部分copy过来;再给这些刚搞过来的产生式后面引入我们的新变量(A‘) ;再以新的(A‘)引出产生式,这些产生式的产生式体部分要么是原来递归产生式体的非递归部分 ,要么是原来递归产生式体的非递归部分 +(A‘) 进行右递归!

先把刹车部分和新引入的写出来;再由新引入的写出 原来递归中的重复部分,以及递归重复部分连接新引入的

技术图片

消除左递归的宏观思路:

消除左递归之前,上图中的(eta)是一直到最后,才因替换而出现;消除左递归之后,上图中的(eta) 一开始很早就出现。

  • 消除间接左递归

替换的过程就是类似于解方程组代入法。通常第一条作为核心方程式,第二条用第一条表示;第三条用第二条和第一条表示。

技术图片

上面最后一部分 是不完整的,因为只用了(A_1)代入,没把(A_2)代入

我们代入的目的是形成直接左递归(暴露出原来隐含的左递归,而且是把已经知道的产生式代入(换句话说,序号小的产生式代入序号大的产生式),比如

  • 上面的 我们先看到(A_1) ,单个(A_1)形不成左递归;

    再看(A_2) ,我们的目的就是把(A_1) 代入到(A_2) 并且还要形成直接左递归的才带入,于是有(A_2->A_3A_1|A_2A_3b|ab) 注意到此时有了直接左递归;我们用消除左递归的方式产生右递归:有 (A_2->A_3A_1|ab|A_3A_1A_2‘|abA_2‘,,, ,,A_2‘->A_3b|A_3bA_2‘)

  • (A_3)发现 我们可能要代入两次(由上面已知的(A_1,A_2,A_2‘)) 先将(A_1)代入(A_3) (A_3->A_2A_3A_2|aA_2|A_3A_3|a) 再把(A_2)代入(A_3) 得到直接左递归形式:(A_3->A_3A_1A_3A_2|A_3A_1A_2‘A_3A_2|abA_3A_2|abA_2‘A_3A_2|aA_2|A_3A_3|a) 然后把这个暴露出直接左递归形式的产生式进行消除左递归。得到 (A_3->abA_3A_2|abA_2‘A_3A_2|aA_2|A_3A_3|abA_3A_2A_3‘|abA_2‘A_3A_2A_3‘|aA_2A_3‘|A_3A_3A_3‘,A_3‘->A_1A_3A_2|A_1A_2‘A_3A_2|A_1A_3A_2A_3‘|A_1A_2‘A_3A_2A_3‘)

正确的应该是:

(A_3->A_2A_3A_2|aA_2|A_3A_3|a=) 再把 递归部分产生式 可以替换的(本例是(A_2)) 用产生式再次替换,得:

(A_3->A_3A_1A_3A_2|abA_3A_2|A_3A_1A_2‘A_3A_2|abA_2‘A_3A_2|aA_2|A_3A_3|a)

经过这样,就把所有的隐含左递归产生式 就给显现出来了!

然后按照正常的 把直接左递归变右递归就行了。


CFG的化简注意事项:

消除(epsilon) 产生式 消除单产生式 消除无用符号

  1. 首先判断原始CFG产生的语言中,有没有(epsilon) 因为我们有一条 【消除(epsilon) 产生式】

也就是说我们化简完了的CFG G,如果原来的产生式有(epsilon) ,那么那化简完由于没有 (epsilon) 产生式 ,所以得∪上(epsilon) 句子(引入新的开始符号(S‘));如果原来语言中就不含$epsilon $ ,那按步骤化简完即可。

  1. 判断 变量 是否是可致空的,所谓 消除(epsilon) 产生式 ,就是(epsilon) 产生式代入这些变量,每一个变量都有两种可能:是(epsilon) ,不是(epsilon) 。比如$A->aBc,B->epsilon $ ,我们判断B是可以为(epsilon) 的,那么这两条产生式就可以变为(A->ac|aBc)

    (A->aBC,B->epsilon,C->epsilon) 就可以变为(A->aBC|aB|aC|a) 这就是***消除(epsilon) 产生式 *** [消(epsilon) 产生式]

  2. 先识别哪些不是单位产生式,找到它!所谓消除单位产生式就是(A->B,B->alpha串) ,直接替代 入B,B实际上没用,得到(A->alpha串)

    (B->A,A->alpha_1|alpha_2|alpha_3) 变为 (B->alpha_1|alpha_2|alpha_3) [消单产生式]

  3. 先判定变量是不是可生成的(每一个变量能够推推推到终结符);再判定变量是可到达的(S变量能够推推推到目前变量) [消无用符号]

  4. 化成乔姆斯基形式 我们的目的是把所有的产生式化成 (A->BC,A->a) 所以第一步,面对产生式体长度大于等于2的,能化成的,尽可能化成这种形式。比如:(A->aAS,B->BbB) 面对这种可以化成的,我们引进(C->a,D->b) 这样就可以化成(A->CAS,B->BDB) (把常量、变量混杂的换成全部变量形式) 这样换了以后,要么右部全部是变量,要么右部只有一个常量

    现在 把变量长度大于等于3的变量串 拆成变量长度是2的。比如:(S->ASB) 就可以拆成(S->AE,E->SB) ;(B->SDS)拆成(B->SG,G->DS) 完成! [化成乔姆斯基范式]

技术图片

化成乔姆斯基范式CNF 接下来我们拿着CNF将其化为GNF,GNF用处还是相当大的,这由GNF的形式所决定 (A->aeta) ,根据终结符(a) 我们可以消除不确定性,可以唯一的替换,避免回溯。

技术图片

替换的目的是由已知推未知(低序号推高序号) 最终会是(Z->Zeta) 这种形式

然后消除直接左递归算法。

技术图片

上面搞完后,会有一个符合要求的GNF范式,我们从这个符合要求的GNF要求进行入手,进行回填,会发现一步步都会产生符合要求的(GNF) ,直到所有的GNF都完成!

技术图片

以上是关于形式语言与编译 九 CFG到CNF再到GNF 左递归消除的主要内容,如果未能解决你的问题,请参考以下文章

节知识点

C语言:分别按如下形式,编程输出九九乘法表。

形式语言自动机—— 文法的一般理论

一段程序从编译到硬件再到执行的过程

编程输出九九乘法表 选1将以矩形方式完整输出 选2输出下三角形式 选0退出 结束程序

编程输出如下形式的九九乘法表: