一看就懂的NULLABLE、FIRST和FOLLOW集合

只需这一篇文章让你轻松理解NULLABLE、FIRST和FOLLOW集合

不看书上的官方解释,我感觉不好理解,下面是我自己总结的一些简单方法:
注:
只要沉下心认真看完这一篇文章,跟着我写的思路一步一步走,仔细理解,我相信是完全能够掌握NULLABLE、FIRST和FOLLOW集合的!!如果有疑问可以留言问我。

NULLABLE集合

定义:
可以推导出空串的非终结符的集合(属于NULLABLE集合意味着不用接受任何输入即可跳过该非终结符)书上的解释,看看了解了就行
举个栗子:

S → ABC | s
A → a | ε
B → C | a
C → c | ε
求NULLABLE集合
① NULLABLE = { A,C }
② NULLABLE = { A,C,B }
③ NULLABLE = { A,C,B,S }

解析:

  1. 第①步中,由于A和C都能直接推导出 ε,所以直接把A和C加入到NULLABLE集合中,此时NULLABLE = { A,C }
  2. 第②步中,由于B可以推导出C,当C取 ε 时,B也能推导出 ε,因此也把B加入到NULLABLE集合中,此时NULLABLE = { A,C,B }
  3. 第③步中,由于S可以推导出ABC,当ABC都取 ε 时,S也能推导出 ε,因此也把S加入到NULLABLE集合中,此时NULLABLE = { A,C,B,S }

(NULLABLE集合比较好理解)
注:
只有当产生式右侧的式子都是非终结符并且都可以推导出 ε,才能把产生式左侧的符号加入到NULLABLE集合中。

FIRST集合

定义:
FIRST(α)被定义为从 α 推导得到的句子的首符号的集合,α 是任意的文法符号串。
自己的理解:
FIRST集合是看箭头后面的第一个字符,如果这个字符是非终结符(大写字母),就继续找这个大写字母能推导出的东西,再看后面是不是非终结符,如果还是,继续找,如果是终结符(小写字母)或空串,则把终结符或空串放到相应的FIRST集合中。
举几个栗子:

例1:
C → ABc
A → a | ε
B → b | ε
求非终结符的FIRST集合
① FIRST(A) = { a,ε }
② FIRST(B) = { b,ε }
③ FIRST(C ) = { a,b,ε }

解析:

  1. 第①步中求FIRST(A),A→a | ε,右侧第一个字符是终结符 a 或 ε ,直接把终结符 a 和 ε放入FIRST(A)集合中,因此FIRST(A) = {a,ε}
  2. 第②步中求FIRST(B), B→b | ε,右侧第一个字符是终结符 b 或 ε ,直接把终结符 b 和 ε 放入FIRST(B)集合中,因此FIRST(B) = {b,ε}
  3. 第③步中求FIRST(C ),C→ABc,右侧第一个字符是非终结符A,因此还要找A能够推导出什么,从题中可知,A→a | ε,当A取 a 时,C→aBc,FIRST(C ) ={a},当A取 ε 时,C→Bc,右侧第一个字符是非终结符B,因此还要找B能够推导出什么,从题中可知,B→b | ε,同理,当B取 b 时,C→bc,FIRST(C)= {b},当B取 ε 时,C→c,FIRST(C ) = {c},综上所述FIRST(C ) = {a,b,c}

例2:
S → AB | bC
A → b | ε
B → aS | ε
C → AB | b
求非终结符的FIRST集合
① FIRST(A) = { b,ε }
② FIRST(B) = { a,ε }
③ FIRST(C ) = { a,b,ε }
④ FIRST(S) = { a,b,ε }

解析:

  1. 第①步中求FIRST(A),A→b | ε,右侧第一个字符是终结符 b 或 ε ,直接把终结符 b 和 ε 放入FIRST(A)集合中,因此FIRST(A) = {b,ε}
  2. 第②步中求FIRST(B), B→aS | ε,右侧第一个字符是终结符 a 或 ε ,直接把终结符 a 和 ε 放入FIRST(B)集合中,因此FIRST(B) = {a,ε}
  3. 第③步中求FIRST(C ),C→AB | b,右侧第一个字符是非终结符A,因此还要找A能够推导出什么,从题中可知,A→b | ε,当A取 b 时,C→bB | b,FIRST(C )={b},当A取 ε 时,C→B | b,右侧第一个字符是非终结符B,因此还要找B能够推导出什么,从题中可知,B→aS | ε,同理,当B取 a 时,C→a | b,FIRST(C )={a,b},当B取 ε 时,C→ε | b,FIRST(C )= {ε,b},综上所述FIRST(C )= {a,b,ε}
  4. 第④步中求FIRST(S),S→AB | bC,右侧第一个字符是非终结符A,因此还要找A能够推导出什么,从题中可知,A→b | ε,当A取 b 时,S→bB | bC,FIRST(S)={b},当A取 ε 时,S→B | bC,右侧第一个字符是非终结符B,因此还要找B能够推导出什么,从题中可知,B→aS | ε,当B取 a 时,S→a | bC,FIRST(S)={a,b},当B取 ε 时,S→ε | bC,FIRST(S)={ε,b},综上所述FIRST(S) = {a,b,ε}

例3:
E → TE’
E’ → +TE’ | ε
T → FT’
T’ → ※FT’ | ε
F → (E) | i
求每个非终结符号的FIRST集合
① FIRST(E) = { (,i }
② FIRST(E’) = { +,ε }
③ FIRST(T) = { (,i }
④ FIRST(T’) = { ※,ε }
⑤ FIRST(F) = { (,i }
注:由于符号两个*之间的内容会被设置成斜体,因此我用※代替,表示一个终结符

解析:

  1. 第①步中求FIRST(E),E→TE’,因此FIRST(E) = FIRST(TE’) = FIRST(T),但是右侧第一个字符是非终结符T,因此还要找T能够推导出什么,从题中可知,T→FT’,因此FIRST(T) = FIRST(FT’) = FIRST(F),所以FIRST(E) = FIRST(T) = FIRST(F)。但是右侧第一个字符是非终结符F,因此还要找F能够推导出什么,从题中可知,F→(E) | i,右侧第一个字符为终结符( 或 i ,直接把终结符( 和 i 加入到FIRST(F)中,因此FIRST(F) = {(,i},所以FIRST(E) = FIRST(T) = FIRST(F) = {(,i}
  2. 第②步中求FIRST(E’),E’→+TE’ | ε,右侧第一个字符是终结符 + 或 ε ,直接把终结符 + 和 ε 加入到FIRST(E’)中,因此FIRST(E’) = {+,ε}
  3. 第④步中求FIRST(T’),T’→※FT’ | ε,右侧第一个字符是终结符 ※ 或 ε ,直接把终结符 ※ 和 ε 加入到FIRST(T’)中,因此FIRST(T’) = {※,ε}

例4:
A → BCc | gDB
B → bCD | ε
C → DaB | ca
D → dD | ε
求非终结符的FIRST集合
① FIRST(A) = { b,d,a,c,g }
② FIRST(B) = { b,ε }
③ FIRST© = { d,a,c }
④ FIRST(D) = { d,ε }

解析:
先求FIRST(B)和FIRST(D),这两个比较简单。

  1. 第②步中求FIRST(B),B→bCD | ε,右侧第一个字符是终结符 b 或 ε ,直接把终结符 b 和 ε 放入FIRST(B)集合中,因此FIRST(B) = {b,ε}
  2. 第④步中求FIRST(D),D→dD | ε,右侧第一个字符是终结符 d 或 ε ,直接把终结符 d 和 ε 放入FIRST(D)集合中,因此FIRST(D) = {d,ε}
  3. 第③步中求FIRST(C ),C→DaB | ca,右侧第一个字符是非终结符D,因此还要找D能够推导出什么,从题中可知,D→dD | ε,当D取 d 时,C→daB | ca,FIRST(C )={d,c},当D取 ε 时,C→aB | ca,右侧第一个字符是终结符 a 或 c ,直接把终结符 a 和 c 放入FIRST(C )集合中,因此FIRST(C ) = {a,c},综上所述FIRST(C ) = {d,a,c}
  4. 第①步中求FIRST(A),A→BCc | gDB,右侧第一个字符是非终结符B,因此还要找B能够推导出什么,从题中可知,B→bCD | ε,当B取 b 时,A→bCc | gDB,因此FIRST(A) = {b,g},当B取 ε 时,A→Cc | gDB,右侧第一个字符是非终结符C,因此还要找C能够推导出什么,从题中可知,C→DaB | ca,第一个字符是非终结符D,因此还要找D能够推导出什么,这同第③步相同,可以将FIRST(C ) 的值放入到FIRST(A),因此FIRST(A) = {d,a,c},综上所述FIRST(A) = {b,d,a,c,g}

FOLLOW集合

定义:
对于非终结符A,FOLLOW(A)被定义为可能在某些句型中紧跟在A右边的终结符号的集合。了解就行,看下面的总结方法
自己的理解:

  1. 对于开始符号,直接把 # 加进开始符号的FOLLOW集合
  2. 要求哪个符号的FOLLOW集合,就把所有包含这个符号的式子找出来,但只有这个符号在箭头右边的才有用
  3. 如果这个符号在最后一个,就把这个符号所在的那个式子的箭头左边的符号的FOLLOW集合加进去
  4. 如果要求的符号的后面还有非终结符,就把这个非终结符的FIRST集合减去空串加进去,此外,如果要求的这个符号的后面的非终结符和箭头左侧的符号相同,则把箭头左侧的FOLLOW集合加入到要求的符号的FOLLOW集合中
  5. 如果要求的符号的后面是终结符,就把这个终结符加到FOLLOW集合中
  6. 在第一个产生式箭头后面的非终结符,都要把开始符号的FOLLOW集合加到相应的FOLLOW集合中

下面举例子来理解:

例1:
S → AB | bC
A → b | ε
B → aS | ε
C → AB | b
求每个非终结符号的FOLLOW集合
① FOLLOW(S) = { # }
② FOLLOW(A) = { a,# }
③ FOLLOW(B) = { # }
④ FOLLOW© = { # }

解析:

  1. 首先求出FIRST集合(后面求FOLLOW集合时会用到)
  2. 第①步中求FOLLOW(S),因此把所有包含S的产生式找出来,只有 S→AB | bC 和 B→aS | ε ,但是只有S在产生式的右侧才有用,因此只有 B→aS | ε 是有用的。根据上面自己的理解的第1条,S是开始符号,因此把 # 加入到FOLLOW(S)中,此时FOLLOW(S) = {#},再根据第3条,在式子B→aS | ε 中,S在最后一个(不考虑ε),因此把箭头左侧的符号B的FOLLOW集合加入到FOLLOW(S)中,此时FOLLOW(S) = {#}+FOLLOW(B),但是FOLLOW(B)还没有算出来,可以先放着;
  3. 第②步中求FOLLOW(A),因此要把所有包含A的产生式找出来,只有S→AB | bC、A→b | ε 和 C→AB | b,但是只有A在产生式的右侧才有用,因此只有S→AB | bC 和 C→AB | b是有用的。根据上面自己的理解的第4条,A的后面还有非终结符B,因此要把B的FIRST集合减去空串加入到FOLLOW(A)中,此时FOLLOW(A) = FIRST(B)-ε = {a},再根据自己理解的第6条,A是由开始符号S推导出来的,因此要把开始符号S的FOLLOW集合加入到FOLLOW(A)中,此时FOLLOW(A) = {a}+FOLLOW(S),但是FOLLOW(S)还没有算出来,可以先放着;
  4. 第③步中求FOLLOW(B),因此要把所有包含B的产生式找出来,只有S→AB | bC、B→aS | ε 和 C→AB | b,但是只有B在产生式的右侧才有用,因此只有S→AB | bC 和 C→AB | b是有用的。根据上面自己的理解的第3条,在产生式S→AB | bC中,符号B在最后一个,因此就把这个式子箭头左侧的符号的FOLLOW集合加入到FOLLOW(B)中,此时FOLLOW(B) = FOLLOW(S),同理符号B在式子C→AB | b中也是最有一个,因此也要把符号C的FOLLOW集合加入到FOLLOW(B)中,此时FOLLOW(B) = FOLLOW(S)+FOLLOW(C ),但是FOLLOW(S)、FOLLOW(A)和FOLLOW(B)都没算出来,也可以先放着;
  5. 第④步中求FOLLOW(C ),因此要把所有包含C的产生式找出来,只有S→AB | bC 和 C→AB | b,但是只有C在产生式的右侧才有用,因此只有S→AB | bC是有用的。根据上面自己的理解的第3条,符号C在最后一个,因此要把这个式子的箭头左侧的符号S的FOLLOW集合加入到FOLLOW(C )中,此时FOLLOW(C ) = FOLLOW(S),但是FOLLOW(S)也还没有算出来,也就先放着;
  6. 通过上面几步发现,没有一个算出来的,但是我们把所有的FOLLOW集合都写下来:
    FOLLOW(S) = {#}+FOLLOW(B)
    FOLLOW(A) = {a}+FOLLOW(S)
    FOLLOW(B) = FOLLOW(S)+FOLLOW(C )
    FOLLOW(C ) = FOLLOW(S)

    此时可以根据最后一个等式:把FOLLOW(C )用FOLLOW(S)代替放入到第三个等式中,此时FOLLOW(B) = FOLLOW(S)+FOLLOW(S) = FOLLOW(S)(这不等同于数学上的加法运算)再把FOLLOW(B)用FOLLOW(S)代替放入到第一个等式中,此时FOLLOW(S) = {#}+FOLLOW(S),因此FOLLOW(S) = {#}。
    所以:
    FOLLOW(S) = {#}
    FOLLOW(A) = {a,#}
    FOLLOW(B) = {#}
    FOLLOW(C ) = {#}

例2:
E → TE’
E’ → +TE’ | ε
T → FT’
T’ → ※FT’ | ε
F → (E) | i
求每个非终结符号的FOLLOW集合
① FOLLOW(E) = { #,) }
② FOLLOW(E’) = { #,) }
③ FOLLOW(T) = { +,#,) }
④ FOLLOW(T’) = { +,#,) }
⑤ FOLLOW(F) = { ※,+,#,) }
注:由于符号两个*之间的内容会被设置成斜体,因此我用※代替,表示一个终结符

解析:

  1. 首先求出FIRST集合(后面求FOLLOW集合时会用到)
  2. 第①步中求FOLLOW(E),因此把所有包含E的产生式找出来,只有 E→TE’ 和 F→(E) | i ,但是只有E在产生式的右侧才有用,因此只有 F→(E) | i 是有用的。根据上面自己的理解的第1条,E是开始符号,因此把 # 加入到FOLLOW(E)中,此时FOLLOW(E) = {#},再根据第5条,E的后面还有终结符,就把这个终结符加入到FOLLOW(E)中,此时FOLLOW(E) = {#,)}
  3. 第②步中求FOLLOW(E’),因此把所有包含 E’ 的产生式找出来,只有 E→TE’ 和 E’→+TE’ | ε ,因为这两个式子中 E’ 都在产生式的右侧,因此这两个式子都是有用的。根据上面自己的理解的第3条,对于 E→TE’ ,E’ 在这个式子的最后一个,因此把这个产生式的左侧的符号E的FOLLOW集合加入到FOLLOW(E’)中(此时也符合自己理解中的第6条),此时FOLLOW(E’) = FOLLOW(E) = {#,)};再看第二个式子E’→+TE’ | ε ,E’ 也在最后一个(不考虑ε),因此也把这个产生式的左侧的符号的FOLLOW集合加入到FOLLOW(E’)中,但这个产生式的左侧符号就是E’ ,因此FOLLOW(E’) = {#,)}
  4. 第③步中求FOLLOW(T),因此把所有包含 T 的产生式找出来,只有 E→TE’ 、E’→+TE’ | ε 和 T→FT’ ,但是只有T在产生式的右侧才有用,因此 E→TE’ 、E’→+TE’ | ε 是有用的。根据上面自己的理解的第4条,这E→TE’ 中,T的后面还有非终结符E’,因此把这个非终结符E’的FIRST集合减去空串加入到FOLLOW(T)中,此时FOLLOW(T) = FIRST(E’)-ε = {+},在E’→+TE’ | ε 中,T后面的非终结符号和箭头左侧的符号相同,因此把箭头左侧符号E’ 的FOLLOW集合加入到FOLLOW(T)中,因此FOLLOW(T) = {+}+FOLLOW(E’) = {+,#,)},再根据自己的理解的第6条,T也是由开始符号E推导出来的,因此也要把开始符号E的FOLLOW集合加入到FOLLOW(T)中,因此FOLLOW(T) = FOLLOW(E) = {+,#,)}
  5. 第④步中求FOLLOW(T’),因此把所有包含 T’ 的产生式找出来,只有 T→FT’ 和T’→※FT’ | ε,但是只有T在产生式的右侧才有用,因此这两个都是有用的。根据自己的理解的第3条,在这两个式子中T’ 都在产生式的最后一个,因此要把产生式左侧的符号的FOLLOW集合加入到FOLLOW(T’)中,因此FOLLOW(T’) = FOLLOW(T)+FOLLOW(T’) = {+,#,)}
  6. 第⑤步中求FOLLOW(F),因此把所有包含 F 的产生式找出来,只有 T→FT’ 、T’→※FT’ | ε 和 F→(E) | i ,但是只有F在产生式的右侧才有用,因此只有 T→FT’ 和 T’→※FT’ | ε 是有用的。根据自己的理解的第4条,在T→FT’ 中,F的后面有一个非终结符T’ ,因此要把这个非终结符T’ 的FIRST集合减去空串加入到FOLLOW(F)中,因此FOLLOW(F) = FIRST(T’)-ε = {※},在T’→※FT’ | ε 中,F后面的非终结符T’ 和箭头左侧的符号相同,因此把箭头左侧的符号T’ 的FOLLOW集合加入到FOLLOW(F)中,因此FOLLOW(F) = {※}+FOLLOW(T’) = {※}+{+,#,)} = {※,+,#,)}

虽然这篇文章有很多文字描述,但是其中的原理很简单,只要记住原理,记住求法就能掌握住这三种集合的求法。

  • 17
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值