求解FIRST集合的2个步骤+求解FOLLOW集合的5个步骤(保证不会有遗漏!!)

写在前面

网上看了很多求解FIRST集合和FOLLOW集合的方法,但是用起来有时候还是会有所遗漏,于是总结了以下方法,只要严格按照这个方法操作,肯定不会找漏。

我们知道,找漏的原因其实就是各种推导总是搞错,如果我们只看产生式就能找到所有集合,而不需要去具体推导,那么就不会遗漏。

求解FIRST集合

由于求解FOLLOW集合涉及到求解FIRST集合,因此先讲下怎么求解FIRST集合。
根据FIRST集合的定义:
在这里插入图片描述
简单来说就是一个非终结符(这里只讨论非终结符)能够推导出一个以终结符开头的串,那么该终结符就属于该非终结符的FIRST集合。由此得到以下算法,两个步骤:

  1. 遍寻所有产生式,找到所有右边为一个终结符开头的产生式A->a…,这里尤其注意,一定不要遗忘产生式右边只有一个终结符的!!很多人看到A->a…就想当然地以为a后边一定要有东西,这是不对的。最后把a加入FIRST(A)。
  2. 遍历所有产生式,找到右边以一个非终结符开头的,这里也要注意A->B也是允许的,B后边不一定要有东西,然后将FIRST(B)加入到FIRST(A)中。

重点在于求FOLLOW集合,我把这个步骤分解成五个步骤:

  1. 将#加入到FOLLOW(S)中,S为文法开始符号。
  2. 遍寻所有产生式,找到所有类似A->…Ba…的产生式,将a加入到FOLLOW(B)中,这里也要注意:B的前面和a的后面可以没有任何东西!!
  3. 遍寻所有产生式,找到所有两个类似A->…BC…的产生式,BC都为非终结符。这里举一个极端例子:A->BCDE,我们将任意两个相邻非终结符对中,后面非终结符的FIRST集合中除空串以外的所有加入到前面非终结符的FOLLOW集合中!简单来说,这里需要分别将C、D、E的FIRST集合加入到B、C、D的FOLLOW集合中。
  4. 遍寻所有产生式,找到类似A->…B的产生式,这里要注意:一定不要遗忘产生式右边只有一个非终结符的,比如A->B这种。然后我们将FOLLOW(A)加入到FOLLOW(B)中。
  5. 遍寻所有产生式,找到所有类似A->…BC的产生式。还是以A->BCDE为例,这里只需要考虑DE的关系,因为必须要求这两个相邻非终结符在最后!!如果FIRST(E)中含有空串,就将FOLLOW(A)加入到FOLLOW(D)中。步骤5其实是步骤4的特殊情况。

注意以上所有过程最好都重复至少两次,防止存在这种情况:比如S要加入E中,S在这一轮加入了新的,E中最后却没有更新。

实战演练一下

在这里插入图片描述

1.求解FIRST集合

1). 根据上面的算法,首先找到所有右边为一个终结符开头的产生式A->a…。我们发现S={a},D={ ϵ \epsilon ϵ},T={b},M={b},H={ ϵ \epsilon ϵ}。
2). 遍历所有产生式,找到右边以一个非终结符开头的。D->STe,于是将S加入D中,D={ ϵ \epsilon ϵ,a};H->M,将M加入H中,于是H={ ϵ \epsilon ϵ,b}
3). 综上:S={a},D={ ϵ \epsilon ϵ,a},T={b},M={b},H={ ϵ \epsilon ϵ,b}。

2.求解FOLLOW集合

1). S={#}。
2). 寻找所有产生式右边非终结符身后跟着一个终结符的产生式。我们发现:D->STe,所以T={e}。
3). 找寻所有右边有两个非终结符相邻的产生式。我们发现:D->STe,所以将FIRST(T)加入S,S={#,b}。
4). 找寻所有右边以一个非终结符结尾的产生式。我们发现:S->aD,所以将S加入D中,D={#,b};T->bM,将T加入M中,M={e};M->bH,将M加入H中,H={e};H->M,将H加入M中,M不变。
5). 找寻所有右边以两个相邻非终结符结尾的产生式,没找到。
6). 综上:S={#,b},D={#,b},T=M=H={e}。

  • 20
    点赞
  • 83
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
求解一个文法的 FIRST 集合FOLLOW 集合编译原理中的重要内容之一。下面提供一些简单的代码来演示如何使用 C 语言求解 FIRST 集合FOLLOW 集合。 首先,我们需要定义一个结构体来表示一个终结符或非终结符。 ```c typedef struct { int is_terminal; // 是否为终结符,0 表示非终结符,1 表示终结符 char symbol; // 符号名称 } Symbol; ``` 然后,我们需要定义一个函数来计算一个产生式的 FIRST 集合。 ```c void compute_first_set(Symbol* production, int length, Symbol* first_set) { int i; for (i = 0; i < length; i++) { if (production[i].is_terminal) { // 如果是终结符,将其加入 FIRST 集合 add_to_set(first_set, production[i]); break; } else { // 如果是非终结符,计算其 FIRST 集合 compute_first_set(get_production(production[i]), get_production_length(production[i]), first_set); if (!has_epsilon(get_production(production[i]))) { // 如果该非终结符不能推导出空串,退出循环 break; } } } } ``` 上面的代码中,`production` 表示一个产生式,`length` 表示该产生式的长度,`first_set` 表示该产生式的 FIRST 集合。 函数中,我们遍历产生式中的每一个符号,如果是终结符,将其加入 FIRST 集合;如果是非终结符,计算其 FIRST 集合,并判断是否能够推导出空串。如果不能推导出空串,退出循环。 接下来,我们需要定义一个函数来计算一个非终结符的 FOLLOW 集合。 ```c void compute_follow_set(Symbol* symbol, Symbol* follow_set) { int i, j; Symbol* production; int length; int index_of_symbol; if (is_start_symbol(symbol)) { // 如果该非终结符是文法的起始符号,将 $ 加入其 FOLLOW 集合 add_to_set(follow_set, create_terminal_symbol('$')); } for (i = 0; i < get_production_count(); i++) { // 遍历文法的所有产生式 production = get_production_by_index(i); length = get_production_length(production); index_of_symbol = find_symbol_in_production(symbol, production, length); if (index_of_symbol >= 0 && index_of_symbol < length - 1) { // 如果该非终结符在产生式中,并且不是最后一个符号 compute_first_set(production + index_of_symbol + 1, length - index_of_symbol - 1, follow_set); // 计算其后面符号的 FIRST 集合 for (j = 0; j < length - index_of_symbol - 1; j++) { // 将其后面符号的 FIRST 集合加入其 FOLLOW 集合 if (has_epsilon(production[index_of_symbol + j + 1])) { continue; } add_to_set(follow_set, production[index_of_symbol + j + 1]); } } else if (index_of_symbol == length - 1) { // 如果该非终结符是产生式的最后一个符号 compute_follow_set(get_left_symbol(production), follow_set); // 计算其左侧非终结符的 FOLLOW 集合,并将其加入其 FOLLOW 集合 merge_set(follow_set, get_follow_set(get_left_symbol(production))); } } } ``` 上面的代码中,`symbol` 表示一个非终结符,`follow_set` 表示该非终结符的 FOLLOW 集合。 函数中,我们首先判断该非终结符是否为文法的起始符号,如果是,将 `$` 加入其 FOLLOW 集合。 然后,遍历文法的所有产生式,找到包含该非终结符的产生式。如果该非终结符不是产生式的最后一个符号,计算其后面符号的 FIRST 集合,并将其加入其 FOLLOW 集合。如果该非终结符是产生式的最后一个符号,计算其左侧非终结符的 FOLLOW 集合,并将其加入其 FOLLOW 集合。 最后,我们可以调用上述函数来求解某个文法的 FIRST 集合FOLLOW 集合。 ```c int main() { init_grammar(); // 初始化文法 compute_all_first_sets(); // 计算文法的所有产生式的 FIRST 集合 compute_all_follow_sets(); // 计算文法的所有非终结符的 FOLLOW 集合 print_all_first_sets(); // 输出文法的所有产生式的 FIRST 集合 print_all_follow_sets(); // 输出文法的所有非终结符的 FOLLOW 集合 return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cyril_KI

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值