上一篇中实现的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. Identity – HTTP Authentication

    前言 HTTP Authentication 是很古老的东西. 已经很少地方会用到了. 但还是给我遇上了. 在做 Google Ads Offline Conversion 时, 它提供了 2 种方式 ...

  2. 【赵渝强老师】Oracle数据库的存储结构

    Oracle的存储结构分为:物理存储结构和逻辑存储结构. 一.物理存储结构:指硬盘上存在的文件 数据文件(data file) 一个数据库可以由多个数据文件组成的,数据文件是真正存放数据库数据的.一个 ...

  3. 排查sshfs挂载失败的问题

    排查sshfs挂载失败的问题 写代码在Linux上运行,但是熟悉的IDE(比如VS code)在自己的电脑上,可以使用sshfs把linux上的目录挂载到本地,再用VScode打开即可,可以使用下面的 ...

  4. Idea启动多服务时的Dashboard展示

    1.提换.idea/workspace.xml文件中的RunDashboard标签 <component name="RunDashboard"> <option ...

  5. DIY Matter Bridge 和智能锁简单联动的实践

    一. 写在前面 在之前的博客文章 <基于乐鑫 ESP32-C3 的 Matter Light 实践>中,我们利用乐鑫的硬件和 SDK 方案实现了简单的 Light 例程,并对 Matter ...

  6. Machine Learning Week_1 Model and Cost Function 5-8

    目录 2.5 Video: Cost Function Intuition-1 unfamiliar words 2.6 Reading: Cost Function Intuition-1 unfa ...

  7. 全面解释人工智能LLM模型的真实工作原理(二)

    前一篇:<全面解释人工智能LLM模型的真实工作原理(一)> 序言:在上一篇文章中,我们从原理上构建了一个识别"叶子"和"花朵"的神经网络,并详细讲解 ...

  8. chrony服务器

    1.安装 yum install chrony -y 2.配置文件 主配置文件:/etc/chrony.conf server ntp.aliyun.com iburst         ---指定阿 ...

  9. 怎样替换 rhel 7.3 的 yum

    [背景] 想在自己安装的虚拟机上搭建一套 git+gitee+vscode 环境(欢迎看下期文章),发现python版本是2.7,这个版本太老 想通过yum进行更新,结果提示需要注册,索性就查查替换y ...

  10. 关于Java后台处理前端用户传来数据过大,超过数据库字段设置长度导致的异常问题处理

    在DTO bean对象上使用javax验证(如最小值,最大值等,请参阅here).