LR分析学习问题整理

LR(0),SLR,LR(1),LALR

  • 首先,LR(0)很简单,就是将文法构造成一个nfa,然后再转成dfa就行。在这个过程中,它可能出现移进-规约冲突和归约归约冲突。一旦出现冲突,则说明该文法无法使用LR(0)。
  • 但对于一类移进-归约冲突,我们实际上可以在原LR(0)的基础上解决掉,如下图

    可以发现,如果下一个终结符在 { a 1 , . . . , a m } \{a_1,...,a_m\} {a1,...,am}中,就移进;如果在 f o l l o w ( B i ) follow(B_i) follow(Bi)中,就归约。只要这些集合不相交即可。这就是SLR。
  • 但是,上面的有待改进,看下图:
    在这里插入图片描述
    我们发现在 I 2 I_2 I2中出现了移进-归约冲突。这个如何解决?我们发现,对于 I 2 I_2 I2,它的困境是:遇到L时不知道是该归约成R还是移进=,而由于 I 2 I_2 I2 I 0 I_0 I0的后继,说明此时在句首,那么可知,当遇到=时说明该使用1),遇到#时该使用2),因此需要通过后面的符号来进行展望,因此,LR(1)加入了1位展望符。
    在这里插入图片描述
  • LALR解决的问题是:LR(1)太大了,每次展望符不同都会产生不同的项目集,但是展望符只在归约时使用,那么为什么不能删去移进时的展望符呢?
    在这里插入图片描述
    我们可以发现,它生成的和SLR一样啊,那这样不会错误的归约吗?
    事实上,它也会报错,但是会推迟报错。
    比如id=id=id,对于LR(1),识别到id=时就到 I 6 I_6 I6了,此后发现输入id到 I 1 2 I_12 I12时,展望符不对,这是就会报错。
    但是对于LALR,它输入id到 I 5 I_5 I5,依旧会归约出L,然后 I 6 I_6 I6遇到L归约出R, I 6 I_6 I6遇到R到了 I 9 I_9 I9,这时候发现,后面展望符不对,此时才会报错。
    LALR分析能力小于LR(1),为什么?同时为什么LALR会引入归约归约冲突而不会引入移进归约冲突

LR的本质

编译原理书中已经提到了,LR(0)实质上是对一个NFA的改进(加了个栈来储存以前的信息),话不多说,直接放图:
在这里插入图片描述
在这里插入图片描述

但对于LR(1),书中却没有提它与NFA的关系,我认为它也是一个NFA,只不过是多加了一个展望符。我自己试验了一个例子,得出的结果与书中一致,因此,我认为,这个结论是正确的。(至于证明我不会,先用再说)

对于LALR,它的想法是合并LR(1)中的一些项目集,其实,合并的项目集都是点在产生式中间或前面的,因为对于这些产生式来说,展望符并不起作用,那么可以知道合并了也不会出现移进归约冲突。

既然我们知道了它们与NFA的关系,那么我们实际上没必要从头开始编写LR分析器,我们可以通过给定一个文法,计/算出所有的项目集,然后再通过项目集以及之间的关系,直接生成LR分析器,这样一来,我们实际上并不需要关心first集(LR(1)需要关心,它需要用first集来计算展望符,参考下面的问题”展望符计算“)和follow集,然后就可以生成分析器。

我的问题:1、LALR和SLR不知道如何通过NFA来实现。2、为什么既然可以在nfa的基础上实现,在书上还有专门介绍不用nfa,而直接生成下推自动机的方法,换句话说,两者有什么区别。

LR如何考虑优先级和结合性

对于一个项目集(状态),如 E − > E + E ∙ E->E+E \bullet E>E+E E − > E ∙ ∗ E E->E\bullet *E E>EE E − > E ∙ + E E->E\bullet+E E>E+E经典的LR分析器是无法处理这些情况的。我们如果想要处理这些二义性文法,就必须得引入一些规则,即优先级和结合性。

对于+和*,显然* 的优先级高,那么在这个状态接收一个*时,它会比较需要归约的产生式中的+,发现比+的优先级高,那么就先执行移进,而不是归约;在接收一个+时,它与需要归约的产生式中的+比较,发现两者优先级一样,那么就查看结合性,如为左结合,那么就优先归约,如为右结合,那么就优先移进。

对应于LR分析表的dfa,即该状态的*边得以保留,而+边被删去。(因为要优先归约,所以事实上不可能移进了)

我的问题:对于该状态,我们很好判断是要与+号比较,但是对于一个普通的情况,是否是跟产生式中的最后一个终结符比较

如何计算first集和follow集

计算first集和follow集的标准算法在编译原理的书上都有,也可以在网上迅速找到,但是,缺点显而易见:它判断是否结束循环的标准是该次循环是否改变了目前所有元素的first集和follow集,如果没有,就重新搜索一遍。可是,对于一些已经计算完成的元素,这么做无疑是浪费时间。

截止目前,我没有查到一个公认的更有效的算法。(可能是大佬觉得这个问题太简单,或者是我查的范围不够广)

我的想法是:我们通过spfa算法的思想,每次如果有元素更新,那么就入队,再通过队列里面记录的元素来更新与它相关的新元素,这样就可以实现对现有算法的优化了。

LR(1)中“……,a”中的a(展望符)如何计算

首先明确一点,比如对于一个项目: E − > T R ∙ , a E->TR \bullet ,a E>TR,aa是属于E的follow集中的元素,它的意思是,如果后面跟着a,那么就选该产生式(这个情况只在点在产生式末尾时有效,否则就应该选点后面的元素)。
显然,在SLR中我们发现,仅简单判断该字符是不是属于follow集,然后再进行选择的方式对于一些情况也不适合,那么一个比较直接的想法就是:我们再将follow集进行细分,对于不同情况选取不同产生式,这个想法的体现就是展望符。

因此,它的计算方法与follow集有关。我们考虑如下文法:
S ′ − > S S'->S S>S S − > B B S->BB S>BB B − > a B B->aB B>aB B − > b B->b B>b
显然有 1 、 S ′ − > ∙ S , # 1、S'->\bullet S,\# 1S>S,#之所以是#,我的理解是S’的follow集只有这个,只能填这个。(想不出更好的解释了)
它等价于下列式子 2 、 S − > ∙ B B , # 2、S->\bullet BB,\# 2S>BB# 3 、 S − > B ∙ B , # 3、S->B\bullet B,\# 3S>BB# 4 、 S − > B B ∙ , # 4、S->BB\bullet ,\# 4S>BB#,这些式子中的#是由于1中的S后面是#,所以,它们期望后面跟一个#。
然后2等价于 5 、 B − > ∙ a B , a / b 5、B->\bullet aB,a/b 5B>aBa/b 6 、 B − > ∙ b , a / b 6、B->\bullet b,a/b 6B>ba/b因为2中B后面跟着B,那么在在接受第一个B后,它们期望后面跟的符号是第二个B的fiest集,即a和b。
3等价于 7 、 B − > ∙ a B , # 7、B->\bullet aB,\# 7B>aB# 8 、 B − > ∙ b , # 8、B->\bullet b,\# 8B>b#因为3中第二个B后面没有符号,那么在接受第二个B后,它们期望的符号应该是3的展望符(因为3的展望符代表接受S后面期望的符号,自然就可以推出此处期望的也是)。
然后以此类推。
在该文法中没有给出一个特殊情况:如果B的first中包含 ε \varepsilon ε怎么办,那2等价于什么,解决方法是:去掉 ε \varepsilon ε,然后再加上下一个元素的first集。

LR如何处理 ε \varepsilon ε产生式

诸如: S − > ε S->\varepsilon S>ε
由于它对应着一个NFA,那么通过NFA中的方法,就可以处理它。

终结符如何识别

遇到终结符,我们通过LR分析表,将能够通过该终结符转移的状态压入状态栈,当遇到一个接受状态时,设该接受状态中产生式右部的个数a,产生式左部符号为X,我们就可以删除状态栈里面的a个状态,然后设此时状态栈栈顶状态为n,我们再查找LR分析表,找到n能通过X转移到的状态,再加入状态栈。

因为LR分析表是一个dfa,因此,每条边都对应着一个元素,那么我们每转移一个元素,就压入一个状态,也就记录了这个元素。而当能够归约时,如果用符号栈(即为人手动模拟),我们需要删掉a个元素,对应状态栈也就是删掉a个,不过事实上删掉的元素不是对应状态,而是两相邻状态之间的边。

接受状态

如果文法给力,不出现移进归约冲突和归约归约冲突,那么一个接受状态中只能包括一个产生式,它的点在最后面。
如果出现移进归约冲突,那么一个接受状态就可以出现多个产生式,其中它有一个产生式点在最后面,其他的点在中间。
如果出现归约归约冲突,那么一个接受状态就会出现多个产生式,它们的点都在最后面。

对于移进归约冲突,我们可以通过增加LR(k)中k,即展望符的个数、定义优先级和结合性来尝试解决;对于归约归约冲突,似乎除了直接修改文法外没有好的解决办法。(还有一种可能的方法:如LR(0)分析器中发现状态n有归约归约冲突,但是如果前面的状态有移进归约冲突,而换了SLR后修改发现无法走到状态n了,这也可以解决,但是我感觉这个可能性太小)也可能是我太菜,没找到。

先LR(1)后LALR能否改变

我的问题:既然一个状态是同心的项目集,就可以被合并,那么可不可以在构造nfa时就给出提示,而不是弄出LR(1)后再减状态
目前想法:既然LR(1)中的非接受状态项目集不考虑展望符,那么能不能再处理完后,构建nfa前,将它们的展望符删去,然后再构建nfa,得到LALR。

select集能否代替first集,它与follow集的关系

这个问题实际上并没有必要,因为编译原理一书中并没有出现select集,但是在哈工大的mooc上老师介绍了select集,如果我没有记错的话,mooc中的LL分析器需要使用的就是select集。

select集实际上是比较简单的。
如果first(E)不包括 ε \varepsilon ε,则select(E)=first(E)。
如果包括,则select(E)=first(E)+follow(E)。
解释:
设符号栈栈顶为非终结符E,select集来判断终结符a是否能位于符号栈栈头。如果a能被E归约,那它显然可以,如果不能被E归约,但是E后面还可以跟非终结符R,而R可以产生a,同时E能产生 ε \varepsilon ε,那它也可以。

first集和follow集结合显然可以计算出select集,而在LL中first集和follow集的作用就是来计算select集,然后应用。但是问题在于,它在LR中没啥用,所以它没啥前途。

移进归约冲突能否通过延后来解决(先读入字符,然后判断执行归约或移进)

这个问题属于我初学时的,现在看有点脑残。
事实上,有延后来解决移进归约冲突的技巧,它叫LR(k),而就算遇到一个冲突的状态,此时不执行移进或归约,而是再读入字符,再进行判断也是没用的。
对于该状态: E − > E + E ∙ E->E+E \bullet E>E+E E − > E ∙ ∗ E E->E\bullet *E E>EE E − > E ∙ + E E->E\bullet+E E>E+E无论此时的输入是1+2还是1+2*3,只要不指定优先级,都没法分析下去。

在构造时,如果展望符为/不为集合,nfa转dfa最后会得到LR(1)吗?

此处我认为是会得到,因为在nfa转dfa时,会经过hopcorft算法。

对于该式 E − > ∙ a B , a / b E-> \bullet aB,a/b E>aBa/b构造时,它实际上是两条: E − > ∙ a B , a E-> \bullet aB,a E>aBa E − > ∙ a B , b E-> \bullet aB,b E>aBb此时,它对应于nfa中的两个状态。
而两者除展望符外的形式相同,所以,通过这两个状态转移到的新状态必然也只有展望符不同,甚至是完全相同,那么在hopcroft算法时,它们就会被认为是无法识别的一个整体,那么最后就会被合并成一个状态,即为最终的状态。

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值