构造LR预测分析表:FIRST与FOLLOW集

1. FIRST 集

顾名思义,“第一个” + “集合”,也就是

FIRST(A) 表示 A 所能推导出的串的首终结符构成的集合

举个例:

有文法:
	A ——> aB
那么 FIRST(A) = {a},因为
	A ——> a...

那么如何求解呢?分三种情况:

  • 能推出空串:

    本文中我用 ‘’ 表示空串

    A ——> B | ''
    那么 '' 应该被加入到 FIRST(A) 中
    
    注:
    如果有
    	A ——> B
    	B ——> ''
    那么 A 也能推出空串,也就是说:
    '' 应该被加入到 FIRST(A) 中。
    
  • 产生式体的首字符为非终结符:

    A ——> B
    A 能推导出的串显然依赖于 B,因此
    FIRST(B) 应该被加入到 FIRST(A) 中
    或者说 FIRST(A) = FIRST(B)
    
  • 产生式体的首字符为终结符:

    A ——> aB
    无论 B 能推导出什么,显然 
    A 能推导出的串的首字符一定是 a;
    因此 a 应该被加入到 FIRST(A) 中。
    
    '这也可以换一种理解方式'FIRST(A) = FIRST(aB) =
    FIRST(a) = { a }
    

来个综合的例子:

(0) E ——> TG
(1) G ——> +TG | ''
(2) T ——> FU 
(3) U ——> *FU | ''
(4) F ——> (E) | id

对于(1)式,由于 E 的产生式体的首字符为非终结符 T,因此有 FIRST(E) = FIRST(T)
同理(2)式有 FIRST(T) = FIRST(F),也就是

FIRST(E) = FIRST(T) = FIRST(F)

那么看 F 的产生式(4)式,由于其产生式体首字符都是终结符,因此 FIRST(F) = { (, id },也就是说

FIRST(E) = FIRST(T) = FIRST(F) = { (, id }

现在还剩(1)、(3)式,两个产生式形式基本相同:产生式体首字符是终结符,能推导出空串,因此:

FIRST(G) = { +, ‘’ }
FIRST(U) = { *, ‘’ }

FIRST集计算完毕。


2. FOLLOW 集

**“跟在后面的” + “集合”,也就是

FOLLOW(A) 表示能出现在 A 的后面的第一个终结符集合

比如

A ——> Ba
对于 B 而言,在这个产生式中,
B 后面紧跟的第一个终结符为 a,
因此 a 应加入 FOLLOW(B)

求解方式分四种:

  • 应将 $ 加入开始符号的 FOLLOW 集

    $ 表示输入结束标记字符

    S ——> A
    A ——> a
    那么 $ 应加入 FOLLOW(S)
    
  • 后面紧跟着终结符

    S ——> Aa
    那么 a 应加入 FOLLOW(A)
    
  • 后面紧跟着非终结符

    S ——> AB
    由于 A 后面是 B,那么
    FIRST(B) 应加入 FOLLOW(A)
  • 产生式体末尾是非终结符

    S ——> ABC
    C ——> ''
    
    1. FOLLOW(S) 应加入到 FOLLOW(C) 中;
    
    由于 S ——> ABC,那么 S 等价于就是 ABC,
    显然 FOLLOW(S) 应加入到 FOLLOW(C); 
    但注意 FOLLOW(S) 不一定等于 FOLLOW(C)"一种理解方式":
    S 和 C 都是 S 文法定义的语言的某语法成分,
    但是 S 显然比 C 更大;
    如果 C 只出现在 S 的末尾,
    那么 FOLLOW(C) = FOLLOW(S);
    但如果 C 还出现在了 S 的中间,比如
    	S ——> ACBC
    那么可能有 FOLLOW(S) < FOLLOW(B) 
    
    2. 如果 
    	C 能推出空串 (或者 '' 属于 FIRST(C))
    那么 
    	FOLLOW(S) 应加入 FOLLOW(B) 中。
    
    当 C ——> '' 时,那么 S 等价于 AB,因此 
    FOLLOW(B) 应加入到 FOLLOW(A) 中。
    
    3. 依次类推,直到出现
       第一个不能推出空串的非终结符
    

同样的例子:

(0) E ——> TG
(1) G ——> +TG | ''
(2) T ——> FU 
(3) U ——> *FU | ''
(4) F ——> (E) | id

FOLLOW集 的分析比 FIRST集 复杂得多,一不小心就容易漏掉个别情况。对此我建议采用以下方法进行求解:

  • 先将 $ 加入 FOLLOW(开始符号)
  • 分析产生式右部
  • 分析产生式整体
  • 从 (0) - (4) 依次分析,如果有 FOLLOW集 改变,执行依次更新操作
  • 将 $ 加入 FOLLOW(开始符号)

    FOLLOW(E) = { $ }

  • 分析产生式(0)的右部

    TG
    

    由于 T 后面紧跟的是非终结符 G,因此 FIRST(G) = { +, ‘’ } 应加入 FOLLOW(T)

    FOLLOW(T) = { + }

  • 分析产生式 (0) 的整体

    E ——> TG
    

    由于 E 相当于 TG,因此 FOLLOW(E) 应加入 FOLLOW(G)

    FOLLOW(G) = { $ }
    为了避免忘记,你自己在分析时做相应的标记,方便后续的分析

    此外,由于 G 能推出空串,因此 E 也相当于 T,所以 FOLLOW(E) 应加入 FOLLOW(T)

    FOLLOW(T) = { $, + }

  • 分析产生式(1)

    +TG | ''
    空串在 FOLLOW集 中没有分析的必要,因此只需要分析:
    +TG
    

    由于 “+TG” 与 “TG” 在形式上基本相同(G都紧跟在T后面,并且G都在末尾),因此(1)的分析结果与(0)的分析结果相同,跳过

    你可以自行验证

  • 分析(2)的右部

    FU 
    

    与(0)的右部分析类似:FIRST(U) = { *, ‘’ } 应加入 FOLLOW(F)

    FOLLOW(F) = { * }

  • 分析(2)的整体

    T ——> FU 
    

    与(0)的整体分析类似:FOLLOW(T) 应加入 FOLLOW(U),FOLLOW(T) 应加入 FOLLOW(F)

    FOLLOW(U) = { $, + }
    FOLLOW(F) = { $, +, * }

  • 分析(3)
    分析结果同(2),跳过

  • 分析(4)的右部

    (E) | id
    'id' 只有终结符,因此不参与分析 
    

    由于 E 后面为终结符 ),因此加入 FOLLOW(E)

    **FOLLOW(E) = { $, ) }

    这里你会发现个问题,我们第一个求解的是 FOLLOW(E),而 FOLLOW(E) 又会被加入到其他的 FOLLOW集 中(比如 FOLLOW(T) 等),那么这时候就需要更新对应的 FOLLOW集,这也是为什么要记录 “谁加入谁的原因”。 但是此时还没分析完,还有一步:分析(4)的整体,因此在分析完一遍后再进行更新操作

  • 分析(4)的整体

    F ——> (E) | id
    

    由于产生式右部的末尾都是终结符,因此没有分析的必要,跳过

  • 更新操作:

    • FOLLOW(E) 应加入 FOLLOW(G)
    • FOLLOW(E) 应加入 FOLLOW(T)
    • FOLLOW(T) 应加入 FOLLOW(U)
    • FOLLOW(T) 应加入 FOLLOW(F)

    因此最后的结果为

    FOLLOW(E) = { $, ) }
    FOLLOW(G) = { $, ) }
    FOLLOW(T) = { $, ), + }
    FOLLOW(U) = { $, ), + }
    FOLLOW(F) = { $, ), +, * }


3. 预测分析表

为了更好地理解此部分,你应该知道如何使用 预测分析表 进行 LR分析

先看两个例子:

现有文法:
	S ——> aA 
	S ——> ''
那么 
	FIRST(S) = { a, '' }
	FOLLOW(S) = { $ }
1. 如果现在输入为 a...,
   需要使用 S 进行分析,
   那么应该使用 
   	   S ——> aA 
   进行语法分析
   
2. 如果现在输入结束了,
   相当于输入为 $,
   需要使用 S 进行分析,
   由于 '' 属于 FIRST(S),
   并且 $ 属于 FOLLOW(S),
   因此因使用
   	   S ——> ''
   进行语法分析
'可以这么理解':
   因为使用 S 去进行分析,但是输入
   却是 FOLLOW(S),也就是说,没有
   遇到 S 能推导出的
       首终结符 (FIRST(S)),
   遇到的是 S 后面'紧跟'的
	   首终结符(FOLLOW(S))
   那么 S 应该被'忽略',也就是
       S 应该换为空串,同时
   由于 S 可以推空串,因此使用 
	   S ——> ''
   进行语法分析

3. 如果输入为 b,
   既不属于 FIRST(S),
   也不属于 FOLLOW(S),
   因此语法分析错误
现有文法:
	S ——> aA
那么 
	FIRST(S) = { a }
	FOLLOW(S) = { $ }
1. 如果现在输入字符为 a ...
   并且需要使用 S 进行分析,
   那么你就知道应该用 
	   S ——> aA
   去进行语法分析

2. 如果输入字符为 $
   需要用 S 进行分析,
   虽然 $ 属于 FOLLOW(S),
   但是 S 不能推空串,
   因此语法分析错误
 
3. 如果输入为 c ...
   需要使用 S 进行分析,
   由于 FIRST(S) 中没有 c
   因此语法分析错误

看了上面两个例子,相信的你对如何构造预测分析表有了基本的理解。下面使用之前的例子以及求出的 FIRST集 与 FOLLOW集 来构造 预测分析表。

基本思路:对文法的每一个产生式的右部 (非终结符),针对不同的终结符,使用对应的 FIRST、FOLLOW集 进行构造


  • 文法

    (0) E ——> TG
    (1) G ——> +TG | ''
    (2) T ——> FU 
    (3) U ——> *FU | ''
    (4) F ——> (E) | id
    
  • FIRST集

    FIRST(E) = FIRST(T) = FIRST(F) = { (, id }
    FIRST(G) = { +, ‘’ }
    FIRST(U) = { *, ‘’ }

  • FOLLOW集

    FOLLOW(E) = FOLLOW(G) = { $, ) }
    FOLLOW(T) = FOLLOW(U) = { $, ), + }
    FOLLOW(F) = { $, ), +, * }

规定:预测分析表横轴为终结符,纵轴为非终结符,中间的元素为指定的产生式(比如 表[S][$] = S —> ‘’,表示当 S 遇到输入字符 $ 时应使用 S —> ‘’ 来进行语法分析)

$
SS —> ‘’

算法:对于每个产生式:A —> B

  • 对于 FIRST(B) 的每个终结符 a,将 A —> B 加入 表[A][a]
  • 如果 ‘’ 属于 FIRST(B),对于 FOLLOW(B) 中的每个终结符 b,将 A —> ‘’ 加入 表[A][b]
  • 其他都是空,表示语法分析错误

  • 先找出所有的终结符

    { id, (, ), +, *, $ }

  • (0) E —> TG
    首先有

    FIRST(E) = { (, id }
    FOLLOW(E) = { $, ) }

    因为 E 与 TG 等效(E —> TG),那么 FIRST(E) = FIRST(TG) = { (, id },因此对于 FIRST(TG) 中的每一个终结符 a,将 E ——> TG 加入 表[E][a](即 表[ E ][ ( ]、表[ E ][ id ])
    故有

    id()+*$
    EE —> TGE —> TG

    由于不存在 E —> ‘’ ,所以不需要考虑 FOLLOW(E)

  • (1) G ——> +TG | ‘’
    首先有

    FIRST(G) = { +, ‘’ }
    FOLLOW(G) = { $, ) }

    同理由于 FIRST(+TG) = { + },因此将 G —> +TG 加入 表[ G ][ + ];
    现在 ‘’ 属于 FIRST(G)了,需要考虑 FOLLOW(G):
    **对于 FOLLOW(G) 中的每一个终结符 b,将 G —> ‘’ 加入 表[ G ] [ b ](也就是 表[ G ][ $ ]、表[ G ][ ) ])

    id()+*$
    GG —> ‘’G —> +TGG —> ‘’
  • 其余的自行分析
    最后结果为

    id()+*$
    EE —> TGE —> TG
    GG —> ‘’G —> +TGG —> ‘’
    TT—> FUT —> FU
    UU —> ‘’U —> ‘’U —> *FUU —> ‘’
    FF —> idF —> (E)

最后

本文一部分来源于课本,一部分为自己的理解,如有错误,欢迎指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/771606.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PhpStorm 2024 for Mac PHP集成开发工具

Mac分享吧 文章目录 效果一、下载软件二、开始安装1、双击运行软件&#xff08;适合自己的M芯片版或Intel芯片版&#xff09;&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功3、打开访达&#xff0c;点击【文…

嵌入式底层系统了解

当裸机功能不复杂的时候&#xff0c;即类似与点亮一个LED灯&#xff0c;驱动LCD和OLED这样的模块&#xff0c;以及各位大学生的搭积木式的毕业设计(狗头保命&#xff09;&#xff0c;此时可以简单地分为硬件和软件层&#xff08;应用层),以及以中间层作为中间联系。 当需要实现…

音视频入门基础:H.264专题(7)——FFmpeg源码中 指数哥伦布编码的解码实现

音视频入门基础&#xff1a;H.264专题系列文章&#xff1a; 音视频入门基础&#xff1a;H.264专题&#xff08;1&#xff09;——H.264官方文档下载 音视频入门基础&#xff1a;H.264专题&#xff08;2&#xff09;——使用FFmpeg命令生成H.264裸流文件 音视频入门基础&…

【SpringCloud】Ribbon源码解析

ribbon是一个负载均衡组件&#xff0c;它可以将请求分散到多个服务提供者实例中&#xff0c;提高系统的性能和可用性。本章分析ribbon是如何实现负载均衡的 1、LoadBalanced 消费者在引入ribbon组件后&#xff0c;给http客户端添加LoadBalanced注解就可以启用负载均衡功能。Lo…

MATLAB贝叶斯线性回归模型案例

采用辛烷值数据集“spectra_data.mat”(任意数据集均可),介绍贝叶斯线性回归模型的构建和使用流程。 运行结果如下: 训练集预测精度指标如下: 训练集数据的R2为: 1 训练集数据的MAE为: 0.00067884 训练集数据的RMSE为: 0.00088939 测试集预测精度指标如下: 测试集数据的R2…

Python学习之小游戏--坦克大作战

今天跟视频学习了Python实现坦克大作战小游戏&#xff0c;挺有意思的&#xff0c;一起来玩吧~ 按空格发射子弹&#xff0c;上下左右键实现移动&#xff0c;ESC键无限复活。 import pygame,time,random from pygame.sprite import Sprite SCREEN_WIDTH800 SCREEN_HEIGHT500 BG…

如何改善提示词,让 GPT-4 更高效准确地把视频内容整体转换成文章?

&#xff08;注&#xff1a;本文为小报童精选文章。已订阅小报童或加入知识星球「玉树芝兰」用户请勿重复付费&#xff09; 让我们来讨论一下大语言模型应用中的一个重要原则 ——「欲速则不达」。 作为一个自认为懒惰的人&#xff0c;我一直有一个愿望&#xff1a;完成视频制作…

typescript2-类的类型

/* 输出 吃饭 游泳 */ []( )继承与多态------------------------------------------------------------------------1. 子类继承父类特征子类 extends 父类2. 当需要父类参数传递时&#xff0c;用子类也可以&#xff0c;这就是多态/* 继承&#xff1a;子类继承父类 多态…

集团型企业组织架构复杂,业务线多,如何进行高效费用管控?

企业管理中流行这样一句话&#xff1a;“企业转型&#xff0c;财务先行”。对集团型企业而言&#xff0c;当今的发展形势下&#xff0c;通过财务战略全面转型、最终撬动企业价值提升&#xff0c;是一件难而正确的事情。 集团企业具有经营规模大、产业链多、分支机构多、地域跨度…

容器部署rabbitmq集群迁移

1、场景&#xff1a; 因业务需要&#xff0c;要求把rabbitmq-A集群上的数据迁移到rabbitmq-B集群上&#xff0c;rabbitmq的数据包括元数据&#xff08;RabbitMQ用户、vhost、队列、交换和绑定&#xff09;和消息数据&#xff0c;而消息数据存储在单独的消息存储库中。 2、迁移要…

中国算力网络市场发展分析

中国算力网络市场发展现状 算力涵盖计算、内存、存储等全方位能力&#xff0c;广泛分布于网络边缘、云计算中心、联网设备及转发节点。随着数字化技术革新&#xff0c;算力与网络正深度融合&#xff0c;推动“算网一体化”的演进。这一新型基础设施日渐凸显其重要性&#xff0c…

番外篇 | YOLOv8改进之即插即用全维度动态卷积ODConv + 更换Neck网络为GFPN

前言:Hello大家好,我是小哥谈。本文所做出的改进是在YOLOv8中引入即插即用全维度动态卷积ODConv和更换Neck网络为GFPN,希望大家学习之后能够有所收获~!🌈 目录 🚀1.基础概念 🚀2.网络结构 🚀3.添加步骤 🚀4.改进方法 🍀🍀步骤1:block.py文件修改…

Kamailio-Web管理页面Siremis的安装与部署

siremis 是针对于 Kamailio 的web管理接口&#xff0c;使用PHP书写&#xff0c;更新至2020年&#xff0c;相对不是太新但是是官方友链的 以下就采用 Ubuntu 22.04Siremis 5.8.0apache http server 2.4php7.0 如有疑问请参看官方指南 以下开始介绍操作步骤 安装apache2.4 we…

一文读懂什么是“GPU算力”

在数字化转型的浪潮中&#xff0c;各行业对算力的需求日益激增&#xff0c;GPU&#xff08;图形处理单元&#xff09;算力作为推动科技进步的重要力量&#xff0c;正逐步从传统的图形渲染领域扩展到人工智能、大数据分析、高性能计算等多个前沿领域。通过本文&#xff0c;深入剖…

亮相2024世界人工智能大会,扫描全能王AIGC“黑科技”助力敦煌遗书数字化修复

7月4日&#xff0c;2024年世界人工智能大会&#xff08;简称“大会”&#xff09;在上海举行。这次这场科技与创新的盛会上&#xff0c;一张古朴、典雅的卷轴吸引了众人的目光。这张被修复的卷轴脱胎于敦煌遗书系列古籍&#xff0c;在被机器拍摄扫描后&#xff0c;卷轴上脏污、…

Airflow: 大数据调度工具详解

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; 欢迎关注微信公众号&#xff1a;野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来&a…

SalesForce集成案例-获取联系人信息

SalesForce本身比较复杂&#xff0c;涉及的东西比较多&#xff0c;下面以使用REST API接口为例&#xff0c;介绍与SalesForce集成的过程&#xff0c;集成案例&#xff1a;获取联系人信息。 首先需要注册一个免费的开发者帐号&#xff0c;具有完全操作SalesForce的权限。 1、注…

Echarts中的热力图和漏斗图(在Vue中使用热力图和漏斗图)

热力图 (Heatmap) Echarts的热力图用于展示两个维度数据矩阵中的值分布情况。它通过在平面上划分成多个矩形区域&#xff0c;并用不同的颜色填充这些区域来表示数据的大小或强度。颜色渐变从浅到深通常映射着数值从小到大&#xff0c;从而直观展示数据的集中程度和分布模式。热…

STM32工业自动化控制系统教程

目录 引言环境准备工业自动化控制系统基础代码实现&#xff1a;实现工业自动化控制系统 4.1 数据采集模块 4.2 数据处理与分析 4.3 控制系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;工业自动化与优化问题解决方案与优化收尾与总结 1. 引言 工业自动化控制系统利用…

INFINI Console 使用介绍

上次在《INFINI Easysearch尝鲜Hands on》中我们部署了两个节点的Easysearch&#xff0c;并且也设置了Console对集群进行监控。那么今天我们再来介绍下INFINI Console的使用。 INFINI Console 仪表盘功能介绍 INFINI Console 是一个功能强大的数据管理和分析平台&#xff0c;…