根据实验描述,可以将下推自动机进行如下设计,得到此PDA的形式化定义:
1. 状态集为{q1,q2,q3,q4,q5,q6,q7}
2. 输入字母表为{a,b,c}
3. 栈字母表为{b,c,$}
4. 起始状态为q1
5. 接受状态集为{q7}
6. 转移函数如下表所示,表中的空白项表示空集
PS:状态图还是当时实验时画的图片,现在就没有重新再画。
在对PDA进行了解之后,实现过程中需要借助到栈,同时考虑到本实验和实验一中的结构上有些类似,因此在实现过程中,便在实验一的基础上进行了调整。将实现过程中,分为了状态转移类和键盘录入及逻辑处理类。
该类主要用于表示转移函数中的各项,代码如下:
public class Old2NewRelation {
private String oldState;//当前状态
private char trans;//读取的字符
private String newState;//新状态
private char top;//栈顶字符
private char putInStack;//要压入栈中的字符
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 Old2NewRelation(String oldState, char trans, String newState, char top, char putInStack) {
super();
this.oldState = oldState;
this.trans = trans;
this.newState = newState;
this.top = top;
this.putInStack = putInStack;
}
/* (非 Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "状态转移关系 [当前状态:" + oldState + ", 识别的字符:" + trans + ", 转移到的状态:" + newState + ", 对栈的操作是用:" + putInStack
+ "替换栈顶元素:" + top + "]";
}
public char getPutInStack() {
return putInStack;
}
public void setPutInStack(char putInStack) {
this.putInStack = putInStack;
}
public char getTop() {
return top;
}
public void setTop(char top) {
this.top = top;
}
}
键盘录入
将输入的字符串特殊处理后存储到字符数组中
//将字符存入字符数组中
Scanner sc = new Scanner(System.in);
String str=sc.next();
str="#"+str;//特殊处理,用来使栈中刚开始识别输入串时栈中栈顶符号为$
char cin[] = str.toCharArray();//利用toCharArray方法转换
sc.close();
初始化状态转移关系集合
private static ArrayList<Old2NewRelation> initRelation() {
//用‘#’表示空字符
//分别表示 当前状态-识别字符-转移状态-栈顶元素-替换栈顶的元素
ArrayList<Old2NewRelation> list = new ArrayList<Old2NewRelation>();
list.add(new Old2NewRelation("q1",'#',"q2",'#','$'));
list.add(new Old2NewRelation("q2",'a',"q4",'#','#'));
list.add(new Old2NewRelation("q2",'b',"q2",'#','b'));
list.add(new Old2NewRelation("q2",'c',"q3",'b','#'));
list.add(new Old2NewRelation("q2",'c',"q6",'#','c'));
list.add(new Old2NewRelation("q2",'#',"q7",'$','#'));
list.add(new Old2NewRelation("q3",'a',"q4",'#','#'));
list.add(new Old2NewRelation("q3",'b',"q2",'#','b'));
list.add(new Old2NewRelation("q3",'c',"q3",'b','#'));
list.add(new Old2NewRelation("q3",'c',"q6",'#','c'));
list.add(new Old2NewRelation("q3",'#',"q7",'$','#'));
list.add(new Old2NewRelation("q4",'a',"q4",'#','#'));
list.add(new Old2NewRelation("q4",'b',"q5",'c','#'));
list.add(new Old2NewRelation("q4",'b',"q2",'#','b'));
list.add(new Old2NewRelation("q4",'c',"q3",'b','#'));
list.add(new Old2NewRelation("q4",'c',"q6",'#','c'));
list.add(new Old2NewRelation("q4",'#',"q7",'$','#'));
list.add(new Old2NewRelation("q5",'a',"q4",'#','#'));
list.add(new Old2NewRelation("q5",'b',"q5",'c','#'));
list.add(new Old2NewRelation("q5",'b',"q2",'#','b'));
list.add(new Old2NewRelation("q5",'c',"q6",'#','c'));
list.add(new Old2NewRelation("q5",'#',"q7",'$','#'));
list.add(new Old2NewRelation("q6",'a',"q4",'#','#'));
list.add(new Old2NewRelation("q6",'b',"q5",'c','#'));
list.add(new Old2NewRelation("q6",'c',"q6",'#','c'));
list.add(new Old2NewRelation("q6",'#',"q7",'$','#'));
return list;
}
从前到后依次遍历读取字符
对于每一个字符都进行下列操作,根据不同的条件,转移到新的状态,并根据实际情况对栈进行出栈或入栈的操作.
//开始处理逐个识别字符,bc中需要选择一个进行压栈,此处选择对b进行操作
for (int i = 0; i < cin.length; i++) {
char cNow = cin[i];
if (cNow!='#') {
System.out.printf("识别第%d个字符%s",i,cNow);
System.out.println();
}else{
System.out.println("预制特殊标志$");
System.out.println("------------------------------------");
}
isTrave=false;
for (Old2NewRelation stateTr : list) {
if (isTrave) {//状态发生转移之后,就不再遍历,而是对下一个字符重新开始判断
break;
}
if(strState.equals(stateTr.getOldState())&&(cNow==stateTr.getTrans())) {
//如果栈空或栈顶字符和当前识别的字符一致(stack.empty()栈的特殊处理也归到这一逻辑中)
if (stack.empty()||((char)stack.peek())=='$'||((char)stack.peek())==cNow) {
if (stateTr.getTop()=='#') {
strState=stateTr.getNewState();//状态转移到新状态
System.out.println(stateTr.toString());
isTrave=true;
if (stateTr.getPutInStack()!='#') {//不管栈是否为空,只要是非空替换空,就入栈
//非空替换空的状态,也就是要压入栈中一个字符
System.out.println("将其压入栈中"+stateTr.getPutInStack());
System.out.println("------------------------------------");
stack.push(stateTr.getPutInStack());
}else {
System.out.println("用空替换空,不对栈进行操作");
System.out.println("------------------------------------");
}
}
}else {//栈中有元素,且和输入字符不同
strState=stateTr.getNewState();//状态转移到新状态
System.out.println(stateTr.toString());
isTrave=true;
if (stateTr.getTop()!='#') {
System.out.println("出栈元素为:"+stack.peek());
System.out.println("------------------------------------");
stack.pop();
}else {
System.out.println("栈顶从空到空,不对栈进行操作");
System.out.println("------------------------------------");
}
}
}
}
}
全部输入字符串识别完成后判断处理特殊符号$
isTrave=false;
if (!stack.empty()&&((char)stack.peek())=='$') {
//根据状态转移函数得到最终处理的状态
for (Old2NewRelation stateTr : list) {
if (isTrave) {
break;
}
if(stateTr.getTop()=='$'&&strState.equals(stateTr.getOldState())&&('#'==stateTr.getTrans())) {
isTrave=true;
strState=stateTr.getNewState();//状态转移到新状态
System.out.println("当前栈顶字符为:"+stack.peek()+"对该标志处理,进行状态转移");
System.out.println(stateTr.toString());
System.out.println("------------------------------------");
}
}
}else {
System.out.println("字符串全部识别后,栈顶字符为:"+stack.peek()+"不是预制的栈标志$");
System.out.println("------------------------------------");
}
if (strState==endState) {
System.out.println("字符b和c在字符串中出现的次数相同,能被该PDA正确接受,字符串全部识别完成后在最终状态"+strState);
} else {
System.out.println("字符b和c在字符串中出现的次数不同,被该PDA拒绝,字符串全部识别结束后当前状态为"+strState);
}
所有处理完成之后,判断当前状态和PDA中的接受状态是否相同,如果相同,表明字符串识别完成后栈中为空并且PDA已经到达了接受状态,这个字符串中b和c出现的次数相同,能够被设计的PDA正确的接受;否则,表明字符串识别完成后栈中非空,并且没有到达设计的PDA的接受状态,这个字符串的b和c出现的次数不同,被设计的PDA拒绝。
构造的PDA能够识别的语言L为字符串中b和c出现的次数相同。
当输入字符串为ccabcbbbca时,bc出现的次数相同,符合语言L的定义,此时应该被本次设计的PDA接受。
输入字符串【仅可包含abc】,判断b的数量是否和c的数量相同,相同接受,不同拒绝
ccabcbbbca
PDA的初始状态为:q1结束接收状态为:q7
------------------------------------
预制特殊标志$
------------------------------------
状态转移关系 [当前状态:q1, 识别的字符:#, 转移到的状态:q2, 对栈的操作是用:$替换栈顶元素:#]
将其压入栈中$
------------------------------------
识别第1个字符c
状态转移关系 [当前状态:q2, 识别的字符:c, 转移到的状态:q6, 对栈的操作是用:c替换栈顶元素:#]
将其压入栈中c
------------------------------------
识别第2个字符c
状态转移关系 [当前状态:q6, 识别的字符:c, 转移到的状态:q6, 对栈的操作是用:c替换栈顶元素:#]
将其压入栈中c
------------------------------------
识别第3个字符a
状态转移关系 [当前状态:q6, 识别的字符:a, 转移到的状态:q4, 对栈的操作是用:#替换栈顶元素:#]
栈顶从空到空,不对栈进行操作
------------------------------------
识别第4个字符b
状态转移关系 [当前状态:q4, 识别的字符:b, 转移到的状态:q5, 对栈的操作是用:#替换栈顶元素:c]
出栈元素为:c
------------------------------------
识别第5个字符c
状态转移关系 [当前状态:q5, 识别的字符:c, 转移到的状态:q6, 对栈的操作是用:c替换栈顶元素:#]
将其压入栈中c
------------------------------------
识别第6个字符b
状态转移关系 [当前状态:q6, 识别的字符:b, 转移到的状态:q5, 对栈的操作是用:#替换栈顶元素:c]
出栈元素为:c
------------------------------------
识别第7个字符b
状态转移关系 [当前状态:q5, 识别的字符:b, 转移到的状态:q5, 对栈的操作是用:#替换栈顶元素:c]
出栈元素为:c
------------------------------------
识别第8个字符b
状态转移关系 [当前状态:q5, 识别的字符:b, 转移到的状态:q2, 对栈的操作是用:b替换栈顶元素:#]
将其压入栈中b
------------------------------------
识别第9个字符c
状态转移关系 [当前状态:q2, 识别的字符:c, 转移到的状态:q3, 对栈的操作是用:#替换栈顶元素:b]
出栈元素为:b
------------------------------------
识别第10个字符a
状态转移关系 [当前状态:q3, 识别的字符:a, 转移到的状态:q4, 对栈的操作是用:#替换栈顶元素:#]
用空替换空,不对栈进行操作
------------------------------------
当前栈顶字符为:$对该标志处理,进行状态转移
状态转移关系 [当前状态:q4, 识别的字符:#, 转移到的状态:q7, 对栈的操作是用:#替换栈顶元素:$]
------------------------------------
字符b和c在字符串中出现的次数相同,能被该PDA正确接受,字符串全部识别完成后在最终状态q7
Process finished with exit code 0
当输入字符串为cbabcac时,bc出现的次数不同,不符合语言L的定义,此时应该被本次设计的PDA拒绝。
输入字符串【仅可包含abc】,判断b的数量是否和c的数量相同,相同接受,不同拒绝
cbabcac
PDA的初始状态为:q1结束接收状态为:q7
------------------------------------
预制特殊标志$
------------------------------------
状态转移关系 [当前状态:q1, 识别的字符:#, 转移到的状态:q2, 对栈的操作是用:$替换栈顶元素:#]
将其压入栈中$
------------------------------------
识别第1个字符c
状态转移关系 [当前状态:q2, 识别的字符:c, 转移到的状态:q6, 对栈的操作是用:c替换栈顶元素:#]
将其压入栈中c
------------------------------------
识别第2个字符b
状态转移关系 [当前状态:q6, 识别的字符:b, 转移到的状态:q5, 对栈的操作是用:#替换栈顶元素:c]
出栈元素为:c
------------------------------------
识别第3个字符a
状态转移关系 [当前状态:q5, 识别的字符:a, 转移到的状态:q4, 对栈的操作是用:#替换栈顶元素:#]
用空替换空,不对栈进行操作
------------------------------------
识别第4个字符b
状态转移关系 [当前状态:q4, 识别的字符:b, 转移到的状态:q2, 对栈的操作是用:b替换栈顶元素:#]
将其压入栈中b
------------------------------------
识别第5个字符c
状态转移关系 [当前状态:q2, 识别的字符:c, 转移到的状态:q3, 对栈的操作是用:#替换栈顶元素:b]
出栈元素为:b
------------------------------------
识别第6个字符a
状态转移关系 [当前状态:q3, 识别的字符:a, 转移到的状态:q4, 对栈的操作是用:#替换栈顶元素:#]
用空替换空,不对栈进行操作
------------------------------------
识别第7个字符c
状态转移关系 [当前状态:q4, 识别的字符:c, 转移到的状态:q6, 对栈的操作是用:c替换栈顶元素:#]
将其压入栈中c
------------------------------------
字符串全部识别后,栈顶字符为:c不是预制的栈标志$
------------------------------------
字符b和c在字符串中出现的次数不同,被该PDA拒绝,字符串全部识别结束后当前状态为q6
Process finished with exit code 0
该实验的代码已上传到github上,可以 点我下载
该代码针对这一实验完成。遇到其他PDA问题时,需要考虑实际,并进行相应的修改。
PS:代码只是简单实现了逻辑,简单构造解决这一实验描述问题的PDA,只是当时解决问题的一种思路。