课程结束了,但是在对这门课的理解上,可能还有不足之处。现在有时间便将当时的实验内容及使用的解决方法记录下来。
实验描述
实验中要求理解NFA工作原理设计数据结构或类,编写代码实现下图中的NFA,通过某些字符串作为输入,通过代码运行判断NFA能否正确接受或拒绝这些字符串,并将得到的结果进行输出。
状态转移函数关系如下表所示,在接下来的分析编程过程中,使用#表示ε字符,用NO表示状态转移为空集的状态。
0 | 1 | 空【用#号表示】 | |
---|---|---|---|
q1 | q1 | q1,q2 | NO |
q2 | q3 | NO | q3 |
q3 | NO | q4 | NO |
q4 | q4 | q4 | NO |
起始项:q1
终止接受状态:q4
程序中对每个状态都进行转移,最后判断结束时,最后状态为q4则接受,状态为其他则拒绝
问题解决
在针对上面的NFA及实验要求进行分析之后,可将整个过程抽象成三个类,分别为状态类,状态转移类,键盘输入及逻辑处理类。
状态类
该类中主要用于表示NFA状态机的初始状态及接受状态,该类的代码如下:
public class State {
private String beginState;//状态机的初始状态
private String endState;//状态机的接收终止状态
public String getBeginState() {
return beginState;
}
public void setBeginState(String beginState) {
this.beginState = beginState;
}
public String getEndState() {
return endState;
}
public void setEndState(String endState) {
this.endState = endState;
}
public State(String beginState, String endState) {
super();
this.beginState = beginState;
this.endState = endState;
}
public State() {
super();
}
}
状态转移关系类
该类主要用于表示状态的转移关系,代码如下:
public class Old2New {
private String oldState;
private char trans;
private String newState;
public String getOldState() {
return oldState;
}
public void setOldState(String oldState) {
this.oldState = oldState;
}
public char getTrans() {
return trans;
}
public void setTrans(char trans) {
this.trans = trans;
}
public String getNewState() {
return newState;
}
public void setNewState(String newState) {
this.newState = newState;
}
public Old2New(String oldState, char trans, String newState) {
super();
this.oldState = oldState;
this.trans = trans;
this.newState = newState;
}
}
键盘输入及逻辑处理类
该类是用来进行一系列的初始化及对字符状态集进行操作处理的类,主要包括以下几项:
- 初始化待识别字符串
通过键盘输入字符串,将字符串保存到字符数组中。 - 初始化状态对象
将状态对象实例化,确定状态机的初始状态和接受状态。public static ArrayList<Old2New> initRelation() { //将拒绝状态用NO表示,将空串用#表示 ArrayList<Old2New> list = new ArrayList<Old2New>(); list.add(new Old2New("q1",'0',"q1")); list.add(new Old2New("q1",'1',"q1")); list.add(new Old2New("q1",'1',"q2")); list.add(new Old2New("q1",'#',"NO")); list.add(new Old2New("q2",'0',"q3")); list.add(new Old2New("q2",'1',"NO")); list.add(new Old2New("q2",'#',"q3")); list.add(new Old2New("q3",'0',"NO")); list.add(new Old2New("q3",'1',"q4")); list.add(new Old2New("q3",'#',"NO")); list.add(new Old2New("q4",'0',"q4")); list.add(new Old2New("q4",'1',"q4")); list.add(new Old2New("q4",'#',"NO")); return list; }
- 初始化状态转移关系集合
将状态机的每个状态转移关系保存到集合中。每一个状态转移关系都是状态转移关系类的一个实例化对象,包含了当前状态,当前识别的字符以及将要转移到的新的状态。 - 从前到后依次遍历读取字符
对于每一个字符都进行下列操作
a)克隆集合,并在克隆前清空克隆的hstmp集合,保证hstmp集合中仅含有当前状态集合中的元素
b)用克隆的hstmp集合进行比较,遍历tmp集合,当前状态下待识别的字符对应有非空集合的状态时,添加到hs集合中.
c)对hs集合进行遍历,有空转移时,将新的状态添加到hs集合中,得到识别当前字符时的空闭包。//遍历集合 for (Iterator iterator = hsTmp.iterator(); iterator.hasNext();) { //获取集合中的状态 String strState = (String) iterator.next(); System.out.println("当前状态:"+strState); for (Old2New stateTr : list) {//遍历所有的状态,由于可能存在多个新的状态,因此需要将这多个新的状态全部记录,对于每个新的状态单独 if(strState.equals(stateTr.getOldState())&&(cin[i]==stateTr.getTrans())) { if (!"NO".equals(stateTr.getNewState())) {//如果状态的转移不为空,将新状态添加到集 hs.add(stateTr.getNewState());//将新的状态添加到集合,先将其记录下来 System.out.println("当前状态:"+stateTr.getOldState()+" 识别字符:"+stateTr.getTrans()+"转移到新状态:"+stateTr.getNewState()+"添加状态:"+stateTr.getNewState()); } } } }
//单独处理,用来得到空闭包 //遍历集合,得到空闭包 for (Iterator iterator = hs.iterator(); iterator.hasNext();) { //获取集合中的状态 String strState = (String) iterator.next(); System.out.println("单独识别空字符,当前状态:"+strState); for (Old2New stateTr : list) {//遍历所有的状态,由于可能存在多个新的状态,因此需要将这多个新的状态全部记录,对于每个新的状态单独 if (strState.equals(stateTr.getOldState())&&('#'==stateTr.getTrans())) { if (!"NO".equals(stateTr.getNewState())) {//如果状态的转移不为空,将新状态添加到集 hs.add(stateTr.getNewState());//将新的状态添加到集合,先将其记录下来 System.out.println("添加状态:"+stateTr.getNewState()); } } } }
所有待识别字符识别处理完成之后,对于最终的ε闭包集合,判断是否包含接受状态,包含则字符串被状态机接受,否则被拒绝。
测试运行
例如输入为11时,从NFA可以看出输入为11时,也会被NFA接受。运行代码效果如下:
请输入仅含[0,1](中间也可输入用#表示的空字符,#可写可不写)待识别的字符串:
11
--------------------------------------------
识别第1个字符1
当前状态:q1
当前状态:q1 识别字符:1转移到新状态:q1添加状态:q1
当前状态:q1 识别字符:1转移到新状态:q2添加状态:q2
单独识别空字符,当前状态:q1
单独识别空字符,当前状态:q2
添加状态:q3
识别当前字符:1,得到的空闭包为{q1,q2,q3,}
--------------------------------------------
--------------------------------------------
识别第2个字符1
当前状态:q1
当前状态:q1 识别字符:1转移到新状态:q1添加状态:q1
当前状态:q1 识别字符:1转移到新状态:q2添加状态:q2
当前状态:q2
当前状态:q3
当前状态:q3 识别字符:1转移到新状态:q4添加状态:q4
单独识别空字符,当前状态:q1
单独识别空字符,当前状态:q2
添加状态:q3
单独识别空字符,当前状态:q3
单独识别空字符,当前状态:q4
识别当前字符:1,得到的空闭包为{q1,q2,q3,q4,}
--------------------------------------------
ε闭包集合中包含接受状态 q4,该字符串被状态机接收
------------------END-------------------
Process finished with exit code 0
代码下载及说明
下载
目前代码已传到github上,可以 点我下载
说明
目前代码可能只是针对这一NFA的解决实现,例如接受状态集合在该实验中只有一个状态,因此对于其他NFA可能需要进行调整,以及需要根据实际的NFA进行状态转移关系集合的初始化。