对编译原理中First、Follow、Select集的一些不算太抽象的理解

2024-03-01 16:50

本文主要是介绍对编译原理中First、Follow、Select集的一些不算太抽象的理解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

对编译原理中First、Follow、Select集的一些不算太抽象的理解

刚开始接触的时候非常懵,不知道是在算什么,在干什么,去询问大佬和老师是否有更生动的理解方式,但是他们都达成一致说反向理解(然而我还是没听懂)

趁着写课后作业的机会再翻了翻书本,看了书上例题,进行了预习回顾,研究了一些个人认为不算太差的适合普通人类大学牲理解的角度,在此记录

下面以一道例题作为例子,核心是通过一个树状推导结构来理解!

example1
有一说一,可以不看上面这图,直接看下面的推导图即可,重在理解!

我们之前学过推导归约,我们这里先用推导,画出一个推导的树状结构

  1. 以S的First为例:

example2

图中红色的非终结符就是S的First集,即{ε,a,b}

来想一想First集含义:First(S)是指S能经过多步得到的左边第一个终结符

当然,既然是终结符,你就会想,有个特殊的终结符ε,需要对他特殊处理吗?

答:暂时放在这里,First集是可以包含ε的,就如上面S的First集里面有空一样,目前先保留着

  1. 下面以另一题的Follow(A)分析为例:

example3
你可以先去书本上看着规则啃一啃,然后会发现,Follow都是看产生式的右边该字母后面跟的是啥终结符,这一点理解很关键,意思就是:如果在上面这颗树中找Follow(A),那么按照规则,就是找到了A后面的B(这符合Follow的含义),然后取First,即First(B).

当然,你可能会想,要是A的是最后一个符号呢?它后面没有上图一样的B,该怎么办?

答:按照书上,继续对产生式左侧求Follow,也即求Follow(S)

总的来说:

  • First(A)就是取A能产生的、第一位是终结符的集合
  • Follow(A)就是取产生式右边紧跟在A后面的那个符号的First集

接下来是重量级Select集合

可以这么说,前面的First和Follow集都是为Select集做铺垫的,他们只是工具,目的只是为了求Select集,因此前面求得基本上不会全用上!!!

Select集含义:找出产生式能推导出的非终结符
我觉得像是对一条产生式能力的评估,找出一条产生式能够有多大的"潜力"(能推导出哪些终结符),这就是所谓“Select集”正在Select的东西

那么我们来想一想,Select集怎么依据前面的First和Follow集得到呢?
或者说,一条产生式的“潜力”怎么通过First和Follow集得到?

回顾一下前面的总结:

First(A)就是取A能产生的、第一位是终结符的集合

Follow(A)就是取产生式右边紧跟在A后面的那个符号的First集

例如有产生式A=>α,那么它的“潜力”有多大?

  1. 我们肯定会想:如果说,α可以在不断地左推能推导到一个第一个符号为终结符的串,那就可以说这个产生式具有这样的“潜力”
  2. 但是,如果α走了什么歪门邪道,在命运的交叉路口选错了方向,最后得到了一整个空串,那么该怎么鉴定它的“潜能”呢?这时,说明A选择α是一个错误的选择,A会寻找自己的Follow,因为根据这条产生式走下去是死路一条
  3. 不管怎么样,Select核心就是找这条产生式能产生的第一个终结符,可以理解为,都是找一个First,但是First找到空的话,就用到Follow,寻找空之后的第一个终结符(当然要是空的话继续找)Follow本质也是找第一个非空的终结符,只是前面的符号推出的都是空串,这个使命就落到了后面的符号身上(即Follow)

​ 于是,这时我才发现大佬和老师为什么说逆向理解了orz,其实归根到底都是为了研究一条产生式能变出什么花来,我们要的是产生式能推出的第一位是终结符的集合,来衡量一条产生式的“潜力”,然后编译器其实就是根据这种“潜力”来选择合适的产生式,进行推导化简的。
​ 其实是先有这个需求,然后生出了“如何合理选择合适产生式”的问题,于是有了Select集,目的是取产生式能推出来的第一位终结符,在之后就有了First集,但是若在First集中遇到空,说明第一个符号“白干了”,一点“潜力”都没有,于是要用到Follow集,也就是相应的“第一个符号不行,就找第二个”,由于第一个符号推出空,那么“整个产生式能推出的第一个终结符”就会由之后的符号的First确定!也就是相应的“第一个符号不行,就找第二个”,由于第一个符号推出空,那么“整个产生式能推出的第一个终结符”就会由之后的符号的First确定!

相信认真读完的你,结合书上的例题,写一写,相信会有不少的收获和更好的理解~

这篇关于对编译原理中First、Follow、Select集的一些不算太抽象的理解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/763138

相关文章

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

Java Spring的依赖注入理解及@Autowired用法示例详解

《JavaSpring的依赖注入理解及@Autowired用法示例详解》文章介绍了Spring依赖注入(DI)的概念、三种实现方式(构造器、Setter、字段注入),区分了@Autowired(注入... 目录一、什么是依赖注入(DI)?1. 定义2. 举个例子二、依赖注入的几种方式1. 构造器注入(Con

Go语言编译环境设置教程

《Go语言编译环境设置教程》Go语言支持高并发(goroutine)、自动垃圾回收,编译为跨平台二进制文件,云原生兼容且社区活跃,开发便捷,内置测试与vet工具辅助检测错误,依赖模块化管理,提升开发效... 目录Go语言优势下载 Go  配置编译环境配置 GOPROXYIDE 设置(VS Code)一些基本

在MySQL中实现冷热数据分离的方法及使用场景底层原理解析

《在MySQL中实现冷热数据分离的方法及使用场景底层原理解析》MySQL冷热数据分离通过分表/分区策略、数据归档和索引优化,将频繁访问的热数据与冷数据分开存储,提升查询效率并降低存储成本,适用于高并发... 目录实现冷热数据分离1. 分表策略2. 使用分区表3. 数据归档与迁移在mysql中实现冷热数据分

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

java使用protobuf-maven-plugin的插件编译proto文件详解

《java使用protobuf-maven-plugin的插件编译proto文件详解》:本文主要介绍java使用protobuf-maven-plugin的插件编译proto文件,具有很好的参考价... 目录protobuf文件作为数据传输和存储的协议主要介绍在Java使用maven编译proto文件的插件

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实