基本原理:
深度优先算法(DFS)。直接一条路走到黑,不行的话再回头,但是如果真的只是等着DFS的递归返回的话,会慢的吓人,所以里面应该加几个判断,在当前这个阶段就可以判断我们推出来的和目标推导式肯定不一样就可以直接返回,这样会快很多。
代码如下:
import java.io.*;
import java.util.*;
class formulate {
private String string; //待推导的字符串
LinkedList<String> grammar= new LinkedList<>(); //文法
LinkedList<String> analysis= new LinkedList<>(); //推导式
formulate(String string) {
this.string = string;
}
String getString(){
return string;
}
}
public class Grammar_Analysis {
private final static int maxlength = 20; //推导的最长步数
private static boolean flag;
private static HashMap<String, LinkedList<String>> list; //全局文法表
private static Set<String> set; //非终结符集
private static formulate f;
private static HashMap<String, LinkedList<String>> grammar() throws IOException{ //得到存储文法的结构
BufferedReader read = new BufferedReader(new InputStreamReader(new FileInputStream(".//data1.txt")));
String str; //保存从文件中读取的文法
LinkedList<String> l; //存储每条文法
HashMap<String, LinkedList<String>> list = new HashMap<>(); //保存所有文法
while((str = read.readLine()) != null) {
String s = str.charAt(0) + ""; //保留开头
String ss = str.substring(4);
String[] str2 = ss.split("\\|"); //每个字符串表示一种文法候选
l = new LinkedList<>(Arrays.asList(str2));
list.put(s, l);
}
read.close();
return list;
}
private static void grammar_Analysis(String string) { //分析推导过程
if (f.analysis.size() >= maxlength) //推导步数过多返回
return;
if (!f.analysis.isEmpty()) { //推导出的长度大于目的推导式或者推导式第一位是终结符但是与目的推导式不等返回
int index = f.analysis.getLast().indexOf(">");
String sss = f.analysis.getLast().substring(index+1);
if ((sss.length()>f.getString().length()) || ((!set.contains(sss.charAt(0)+"")) && f.getString().charAt(0) != sss.charAt(0)))
return;
}
if (string.equals(f.getString())) { //推导成功
flag = true;
return;
}
for (int i = 0; i < string.length(); i++)
if (set.contains(string.charAt(i) + "")) { //是非终结符,可继续推导
for (String l : list.get(string.charAt(i) + "")) { //对可推导的每一个候选式进行选择
String str = string;
str = str.replaceFirst(str.charAt(i) + "", l);
f.grammar.addLast(string.charAt(i) + "-->" + l);
f.analysis.addLast(string + "-->" + str);
grammar_Analysis(str);
if (flag)
return;
else {
f.grammar.removeLast();
f.analysis.removeLast();
}
}
}
}
private static void print(){ //输出函数
System.out.println("使用的文法\t\t推导式");
for (int i = 0; i < f.grammar.size(); i++) {
System.out.println(f.grammar.get(i) + "\t\t\t" + f.analysis.get(i));
}
System.out.println();
}
public static void main(String[] args) throws IOException{
list = grammar(); //文法HashMap<String, LinkedList<String>>
set = list.keySet(); //非终结符集
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(".//data2.txt")));
String str;
while ((str = reader.readLine()) != null) {
f = new formulate(str);
flag = false;
grammar_Analysis("S");
System.out.println("本次待推导式为:" + f.getString());
print();
}
reader.close();
// Scanner sc = new Scanner(System.in);
// System.out.println("请输入待推导式:");
// String str = sc.nextLine();
// f = new formulate(str);
// flag = false;
// grammar_Analysis("S");
// System.out.println();
// print();
// sc.close();
}
}
得到的知识点:
1.LinkedList<String> l = new LinkedList<>(Arrays.asList(str))
可直接将字符串数组转为字符串链表
2.正则表达式中如果含有“|”的话,需要加双斜杠进行转义
3.如果使用类中的成员,在循环中调用了并且改变了值的情况下,每次调用前要重新赋值
4.编译器肯定不会错,肯定是程序错了。