上一篇中实现的First函数没有考虑左递归,在这对此说明和实现

1.立即左递归

A -> Ab|a

1.两步或两步以上产生的左递归

A -> Bc|a

B -> Ab|d

前面的实现中,递归用first函数

public Set<Terminal> first(List<Symbol> tokens) {
first(token)
}
public Set<Terminal> first(Symbol token) {
first(tokens)
}

如果产生式存在左递归就会出现循环调用,得不到结果

那么需要做下面处理

1. 需要记录当前外层有没有处理这个符号,如果有则跳过

2. A -> Bc|a B->Ad | ε

1)first(A)

2)first(Bc)  并 first(a)

3)first(B) frist(c)  并  first(a)

4)first(Ad) frist(ε) first(c) 并 first(a)

5)first(A) first(d) frist(ε) first(c) 并 first(a)

可以看第5步又需要first(A),因为外层正在处理,所以直接返回,但是返回前需要判断A是否为空,

如果不为空,则直接返回,如果为空,则继续first(d)

看两组产生式的结果

A -> Bc|a B->Ad| d | ε               A的first集合是 a, d, c, B的first集合 a d c

A -> Bc|a B->Ad | d               A的first集合是 a, d, B的first集合 a d

可以看出  A -> Bc 对 frist(A)集合,如果A, B可以推出 ε 那么集合就多了c,否则 A->Bc 不影响 first(A)的结果

所以需要加过一个函数专门用来判断A是否可以推导出ε

/**
* tokens是否可以推导出ε
* @param tokens
* @param expands
* @return
*/
public boolean hasEpsilon(List<Symbol> tokens, Set<Symbol> expands) {
for (Symbol token : tokens) {
if (expands.contains(token)) {
return false;
}
if (Terminal.epsilon.equals(token)) {
return true;
}
if (token instanceof Terminal) {// 终结符直接返回
return false;
} expands.add(token);
boolean hasEpsilon = false;
List<Production> productionList = getProduction((Nonterminal) token);
for (Production production : productionList) {
if (hasEpsilon(production.getBody(), expands)) {
hasEpsilon = true;
break;
}
}
if (!hasEpsilon) {
return false;
}
expands.remove(token);
}
return true;
}

最终First集合函数修改如下

/**
* P140 FIRST(X)集合
* @param tokens 正在计算的串
* @param expands 用于记录正在计算的串是由哪个非终结符展开得到的
* @return
*/
public Set<Terminal> first(List<Symbol> tokens, Set<Symbol> expands) { // 是否全部包含ε
boolean allContainsEpsilon = true;
Set<Terminal> result = new HashSet<>();
for (Symbol token : tokens) { Set<Symbol> epsilonExpands = new HashSet<>();
boolean hasEpsilon = hasEpsilon(Arrays.asList(token), epsilonExpands); Set<Terminal> firstSet = first(token, expands);
if (firstSet == null) {
if (hasEpsilon) {
continue;
} else {
return result;
}
} else {
for (Terminal terminal : firstSet) {
if (terminal.equals(Terminal.of("ε"))) {
continue;
}
result.add(terminal);
} if (!hasEpsilon) {
allContainsEpsilon = false;
break;
}
}
} // 如果对于所有的j=1,2,...,k,ε在FIRST(Yj)中,那么将ε加入到FIRST(X)
if (allContainsEpsilon) {
result.add(Terminal.of("ε"));
} return result;
}

编译器-FIRST集合(补充:左递归)的更多相关文章

  1. 常用对象API、附加:集合补充

    基本数据类型对象包装类: 为了方便操作基本数据类型值,将其封装成了对象,在对象中定义了属性和行为丰富了该数据的操作. 用于描述该对象的类就称为基本数据类型对象包装类. byte——Byte short ...

  2. Python 之父的解析器系列之五:左递归 PEG 语法

    原题 | Left-recursive PEG grammars 作者 | Guido van Rossum(Python之父) 译者 | 豌豆花下猫("Python猫"公众号作者 ...

  3. python学习笔记:第14天 内置函数补充和递归

    一.匿名函数 匿名函数主要是为了解决一些简单需求而设计的一种函数,匿名函数的语法为: lambda 形参: 返回值 先来看一个例子: # 计算n的n次方 In[2]: lst = lambda n: ...

  4. python 内置函数补充 or 递归 or 二分法

    一.内置函数的补充 repr() 显示出字符串的官方表示形式 chr() print(chr(20013)) # 把数字编码转换成字符串 ord() print(ord('中')) # 20013 把 ...

  5. JavaSE 集合补充点(JDK1.9对集合添加的优化)

    通常,我们在代码中创建一个集合(例如,List 或 Set ),并直接用一些元素填充它. 实例化集合,几个 add方法调用,使得代码重复. public class Demo01 { public s ...

  6. Java 集合补充

    集合大致可以分List,Set,Queue,Map四种体系. 集合和数组不一样,数组元素可以是基本类型的值,也可以是对象(的引用变量),集合里只能保存对象(的引用变量). 访问:如果访问List集合中 ...

  7. 消除左递归c语言文法

    <程序> -〉 <外部声明> | <函数定义><外部声明> -〉<头文件> | <变量> | <结构体> <头 ...

  8. C程序语法(无左递归)

    <程序> -〉 <外部声明> | <函数定义><外部声明> -〉<头文件> | <变量> | <结构体> <头 ...

  9. java集合-补充HashMapJDK1.8

    在Java 8 之前,HashMap和其他基于map的类都是通过链地址法解决冲突,它们使用单向链表来存储相同索引值的元素.在最坏的情况下,这种方式会将HashMap的get方法的性能从O(1)降低到O ...

  10. C语言简易文法(无左递归)

    <程序> -〉 <外部声明> | <函数定义><外部声明> -〉<头文件> | <变量> | <结构体> <头 ...

随机推荐

  1. 记一次 .NET某上位机视觉程序 卡死分析

    一:背景 1. 讲故事 前段时间有位朋友找到我,说他的窗体程序在客户这边出现了卡死,让我帮忙看下怎么回事?dump也生成了,既然有dump了那就上 windbg 分析吧. 二:WinDbg 分析 1. ...

  2. Python 潮流周刊#69:是时候停止使用 Python 3.8了(摘要)

    本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...

  3. 5G网络架构的演进趋势

    5G网络架构的演进趋势 概述 5G移动通信网络系统包括5GC(5G Core Network,5G核心网)和NG-RAN(Next Generation Radio Access Network,5G ...

  4. 借助 Flutter 顺畅地开发多平台应用

    Flutter 已于近期发布了 Flutter 2,Flutter 和 Dart 的产品总监 Tim Sneath 在 2021 年三月上旬举办的 Flutter Engage 活动中表示,Flutt ...

  5. duxapp:基于Taro使用模块化开发,提升开发效率

    duxapp是基于Taro二次开发的模块化框架 使用这个框架,结合框架提供的UI库和工具库,能帮助你快速且高质量的完成项目,且能实现同时开发小程序.H5.APP(React Native),并且保证各 ...

  6. [OI] 容斥原理拓展

    10.容斥原理拓展 10.1 二项式反演 \[P.10.1(1) \] 设 \(U=\{S_1,S_2,S_3...S_n\}\),且任意 \(i\) 个元素的交集都相等 定义 \(g(x)\) 为 ...

  7. LINQ 统计字符频率

    var arr = new string[] {"test","zhulongxu","asdfdgd","yangmi" ...

  8. js中判断数据类型的方法有哪些

    判断数据类型可以使用 typeof 但是typeof 判断数组和函数时返回的都是Object 不能具体判断,这时使用 instanceof 可以判断对象是否是另一个函数创造的 : 用法: typeof ...

  9. 用Qt发布打包的应用程序

    使用Qt Creator创建Realase版本的程序之后,我们的程序可以正常运行,但是在没有Qt的电脑里是无法运行的. 原因说的很清楚,缺少了动态库.所以我们要把其他需要的库打包进来一起发布才可以.Q ...

  10. Web渗透03_扫描技术

    前言 在获取目标主机的各类信息时,有一些综合工具可以一键扫描完成,大大方便了测试效率.但扫描结果并非100%的正确,需要我们的判断. namp NMAP(Network Mapper)是一款开放源代码 ...