人工智能导论实验——基于产生式的动物识别专家系统

目录

第一部分:实验报告

一、实验目的

 二、实验环境

三、实验步骤/过程

1. 数据结构的定义与构建

2. GUI界面设计

3. 规则与事实的输入输出

(1) 从文本中导入规则并在界面中显示

(2) 在界面中输入规则并显示

(3) 将规则保存到文本中

(4) 在界面中输入事实并显示

(5) 其他操作:清空事实库

4. 推导过程的实现

(1) 基本逻辑

(2) 初步实现

(3) 进一步完善

5. 其他问题的说明与解决

(1) 文件导入失败

(2) 输入规则后当最后一个前件仅占1个字符时无法被读取

四、实验结果

1. 输入事实“会飞”、“下蛋”、“善飞”的推导结果

2. 输入事实“会飞”、“下蛋”的推导结果

3. 输入事实“信天翁”的推导结果

4. 重复输入事实的提示

5. 重复输入规则的提示

6. 插入规则“鸟^A^B->C”,此规则不是最终规则

7. 在插入上述规则后,输入事实“鸟”、“A”、“B”

第二部分:源代码

1. package solution

1.1 Infer.java 

1.2. Node.java

2. package window

2.1 Answer.java

2.2 Interaction.java

2.3 Msgbox.java


第一部分:实验报告

一、实验目的

  1. 理解和掌握产生式知识表示方法及产生式系统的基本过程,能够利用编程技术建立一个基于产生式知识表示的简单的专家系统(可采用前向推理)。
  2. 建立一个动物识别系统的规则库,用以识别虎、豹、斑马、长颈鹿、企鹅、鸵鸟、信天翁等7种动物 (至少7种动物,规则可增加)。

 二、实验环境

Eclipse IDE for Eclipse Committers 2023-03 (4.27.0)

JDK 17

三、实验步骤/过程

此系统基于Java语言开发,使用WindowBuilder插件设计GUI界面。

1. 数据结构的定义与构建

使用Java自带的LinkedList类作为链表,在事实库中选择String类作为链表节点类型,在规则库中使用自己构建的Node类作为链表节点类型。Node类属性的定义如下所示。

private LinkedList<String> fro = new LinkedList<String>();

private String arf;

private int level = -1;

private boolean isUse = false;

其中,链表fro存储规则中的各个前件;字符串arf存储规则后件;level存储该规则是否为最终规则,-1代表无效,0代表不是最终规则,1代表是最终规则;isUse存储该规则是否被使用,false代表未被使用,true代表已被使用。

下面给出Node类中的方法与功能介绍

public Node()  无参构造方法

public Node(LinkedList<String> a, String b, int c, boolean d)   带有4个参数的构造方法,其中a传入fro时是深拷贝

public boolean equal(Node node)   结点判等方法,若节点中的属性均相等,则返回true

public LinkedList<String> getFro()  fro属性的getter方法

public void setFro(LinkedList<String> fro)  fro属性的setter方法(深拷贝)

public String getArf()  arf属性的getter方法

public void setArf(String arf)  arf属性的setter方法

public int getLevel()  level属性的getter方法

public void setLevel(int level)  level属性的setter方法

public boolean isUse()  isUse的getter方法

public void setUse(boolean isUse)  isUse的setter方法

2. GUI界面设计

GUI界面主要在Interaction.java中实现。下面给出该系统界面的样式设计截图,以及其中部分控件的名称。

图1  系统界面的样式设计

除此之外,还在Msgbox.java、Answer.java中分别实现了信息提示与结果显示页面,下面给出这两个页面的设计样式。 

图2  Msgbox.java:信息提示窗口

图3  Answer.java:结果显示窗口

请注意,这两个窗口的标签中的内容是动态的,可以从外部传入字符串并在其中显示。

3. 规则与事实的输入输出

规则与事实的处理、推导等操作主要在Infer.java中完成。Infer类属性的定义如下

private LinkedList<String> DataBase = new LinkedList<String>();

private LinkedList<Node> RuleBase = new LinkedList<Node>();

DataBase代表综合数据库,即事实库,而RuleBase代表规则库。下面给出几个常用的Infer类方法与功能介绍

public LinkedList<String> froString2Lst(String fro)  以“&”作为分隔符,将字符串拆分,依次放入到LinkedList中

public String processLst2String(LinkedList<Node> lst)  将LinkedList中的各元素依次连接起来,形成一个字符串

public void sortRB()  根据规则前件数量的多少,对规则库中的规则进行升序排序

public boolean loadRB()  从文本中导入规则库,导入成功返回true,否则返回false

public boolean saveRB(LinkedList<String> lst, String str, int level)  将规则保存到文件中,保存成功返回true,否则返回false

public void clearDB()  清空事实库

public void resetRB()  重置规则库中每条规则的isUse属性为false

public boolean existInDB(String str)  判断该事实是否已经存在于事实库中,若存在则返回true,否则返回false

public boolean existInRB(Node node)  判断该规则是否已经存在于规则库中,若存在则返回true,否则返回false

public boolean preInsertDB(String str)  向事实库中插入事实,插入之前判断事实是否已经存在。若成功插入返回true,否则返回false

public boolean preInsertRB(LinkedList<String> fro, String arf, int level)  向规则库中插入规则,插入之前判断规则是否已经存。若成功插入返回true,否则返回false

public boolean mainProcess()  主推导进程,推到成功返回true,否则返回false

注意,为了将界面显示与事实推导连接起来,需要在界面类Interaction中添加private Infer infer属性。下面详细介绍有关方法的使用与实现。

(1) 从文本中导入规则并在界面中显示

在文本中,规则以下列样式保存。

哺乳动物&食肉动物&黄褐色&暗斑点->金钱豹_1

规则的每个前件之间用“&”连接,前件与后件之间用“->”连接,后件紧接着“_x”,x可取0和1,当规则是最终规则时,x取1,其他情况下,x取0.为了在系统打开时导入规则,需要在Interaction类的构造函数中添加infer.loadRB();语句。下面介绍loadRB方法的实现。

按行读取文件,首先在每行中搜索“-”的位置,根据此位置,提取出前件。然后,在每行中搜索“_”的位置,根据此位置,提取出后件与level标记。存有前件的字符串再经froString2Lst方法加工处理,拆分成LinkedList的形式。最后,经过preInsertRB方法向规则库中插入,并使用sortRB方法排序,完成操作并返回true。在读取文件时可能会出现异常,若出现异常,通过Msgbox类窗口给出提示,并返回false。

导入完成后,读取规则库,并在textArea_RB中显示。

(2) 在界面中输入规则并显示

在界面中的textField_RB_FRO文本框中输入前件,在textField_RB_ARF文本框中输入后件,radioButton1和radioButton0选择规则后件类型。为button2按钮绑定事件:读取文本框中的数据,若存在空串则终止事件。若选中radioButton1,将level设定为1,若选中radioButton0,则将level设定为0.使用froString2Lst方法加工所获得的前件字符串,并使用preInsertRB方法向规则库中插入。若插入成功,使用saveRB方法保存该规则,并清空textArea_RB,重新读取并写入规则。若插入失败,通过Msgbox类窗口给出提示,提示用户出现重复元素。

(3) 将规则保存到文本中

通过saveRB方法将规则保存到文本中,下面简要介绍saveRB方法的实现。

首先通过sortRB方法对规则库进行排序。随后打开文本,追加规则信息,操作完成后返回true。若出现异常,通过Msgbox类窗口给出提示,并返回false。

(4) 在界面中输入事实并显示

在界面中的textField_DB文本框中输入事实。为button1按钮绑定事件:读取文本框中的数据,若存在空串则终止事件。使用preInsertDB方法向事实库中插入事实。若插入成功,向textArea_DB插入事实;若插入失败,通过Msgbox类窗口给出提示,提示用户出现重复元素。

(5) 其他操作:清空事实库

为button4按钮绑定事件:使用clearDB方法清空事实库,并同时清空textArea_DB中的数据。

4. 推导过程的实现

推导过程主要由Infer类中的mainProcess方法实现。此外,还需要向button3按钮绑定事件:执行mainProcess方法,清空textArea_DB中的数据,重新读取事实库并输出。下面详细介绍推导过程的实现。

(1) 基本逻辑

① 初始化事实库,把问题的初始已知事实送入事实库中(已在前文实现);

② 若规则库中存在尚未使用过的规则,而且它的前提可与事实库中的已知事实匹配,则转第③步,若不存在这样可与之匹配的事实,则转第⑤步; 若规则库中不再有未使用的规则,则终止问题的求解过程。

③ 执行当前选中的规则,并对规则作上标记,把该规则执行后得到的结论送入事实库中。如果该规则的结论部分指出的是某些操作,则执行这些操作;

④ 检查事实库中是否已包含了问题的解,若已包含,则终止问题的求解过程,否则转第②步;

⑤ 要求用户提供进一步的关于问题的已知事实,若能提供,则转第②步,否则终止问题的求解过程。

(2) 初步实现

首先创建一个最外层的大循环,当已经找到结论或规则库中不再有未使用的规则的情况下结束循环。先查询事实库中有没有结论,创建两层循环遍历事实库与规则库中的结论,若有则提前终止,通过Answer类窗口输出结果,并提示事实库中存在结论。

正常情况下,受数据结构的影响,需要创建三层循环来让事实依次匹配每条规则的前提。第一层循环确定规则库中的第几条规则,并比较已匹配的前件个数和实际的前件个数;若相等,记录该规则的编号,并提前结束循环。第二层循环确定这条规则的第几个前件。第三层循环确定事实库中的第几条事实,并进行匹配。

一轮匹配结束后,进行分情况讨论。若未记录到规则编号,说明不存在可与之匹配的前提,给出中断标记,并终止推导的大循环。若通过规则编号找出该规则是最终规则,保存结论,将该规则放入事实库和推导过程中,终止推导。除了上面的两种情况下,通过规则编号标记该规则已被使用,将该规则放入事实库和推导过程中。

在推导结束的情况下,通过Answer类窗口对结果进行输出。如果有中断标记,输出时添加补充事实的提示和推导过程,并通过resetRB方法重置规则库。如果成功推出,给出结论和推导过程,并通过resetRB方法重置规则库。

(3) 进一步完善

为提高推导效率,可以进行冲突消解的措施。受限于自己的学识与能力,冲突消解策略选择的是对规则库按条件个数排序来实现。排序方法sortRB已经在前文中提到过,该方法根据规则前件数量的多少,对规则库中的规则进行升序排序;若两规则前件数量相等,按先出现的优先在前,即此时按默认顺序排序。

5. 其他问题的说明与解决
(1) 文件导入失败

loadRB方法的bug,文件尾部有空行时会因为调用length方法造成空指针异常而无法正常读取文件。

 

图6 修改前代码(左)与修改后代码(右)的对比(loadRB方法)

更改循环的终止条件,添加当读取行为空行时终止读取,即可解决该问题。

(2) 输入规则后当最后一个前件仅占1个字符时无法被读取

froString2Ls方法的bug,在分隔时没考虑到这种情况。

 

图7 修改前代码(左)与修改后代码(右)的对比(froString2Ls方法)

当读到最后一个“&”时,i++,下次循环后i++,使得i==len,循环提前终止,从而读取不到字符。将i = i + 1;语句删除即可成功读取字符。

四、实验结果

以下为规则库数据。

有毛发->哺乳动物_0

有奶->哺乳动物_0

会飞&下蛋->鸟_0

吃肉->食肉动物_0

有犬齿&有爪&眼盯前方->食肉动物_0

哺乳动物&有蹄->有蹄类动物_0

哺乳动物&嚼反刍动物->有蹄类动物_0

哺乳动物&食肉动物&黄褐色&暗斑点->金钱豹_1

哺乳动物&食肉动物&黄褐色&黑色条纹->虎_1

有蹄类动物&长脖子&长腿&暗斑点->长颈鹿_1

有蹄类动物&黑色条纹->斑马_1

鸟&长脖子&长腿&黑白二色&不飞->鸵鸟_1

鸟&会游泳&不飞&黑白二色->企鹅_1

鸟&善飞->信天翁_1

1. 输入事实“会飞”、“下蛋”、“善飞”的推导结果

图8

2. 输入事实“会飞”、“下蛋”的推导结果

图9

3. 输入事实“信天翁”的推导结果

图10

4. 重复输入事实的提示

图11

5. 重复输入规则的提示

图12

6. 插入规则“鸟^A^B->C”,此规则不是最终规则

 

图13

左侧为插入新规则之前的情况,右侧为插入新规则之后的情况。插入规则后,规则库自动根据前件数量进行升序排序。

7. 在插入上述规则后,输入事实“鸟”、“A”、“B”

图14

第二部分:源代码

1. package solution

1.1 Infer.java 
package solution;

import window.Msgbox;
import window.Answer;

import java.util.LinkedList;
import java.util.Comparator;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.BufferedWriter;

public class Infer {
	// Attributes
	private LinkedList<String> DataBase = new LinkedList<String>();
	private LinkedList<Node> RuleBase = new LinkedList<Node>();
	
//	private LinkedList<Node> Process = new LinkedList<Node>();	// 保存推理过程
//	private boolean isInterrupt = false;	// 推理是否中断(未找到最终结论)
	
	// Methods
	public int getRBsize() {
		return RuleBase.size();
	}
	
	public int getDBsize() {
		return DataBase.size();
	}
	
	public String getRuleBase(int index) {		
		String str = RuleBase.get(index).getFro() + " -> " + RuleBase.get(index).getArf() + " ("+ RuleBase.get(index).getLevel() + ")\n"; 
		return str;
	}
	
	public String getDataBase(int index) {
		return DataBase.get(index);
	}
	
	public LinkedList<String> froString2Lst(String fro){
		LinkedList<String> temp = new LinkedList<String>();
		int len = fro.length();
		int start = 0;
		for(int i=0;i<len;i++) {
			if(i == len-1) {
				temp.add(fro.substring(start, i+1));
				break;
			}
			if(fro.charAt(i) == '&') {
				temp.add(fro.substring(start, i)); 
				start = i+1;
			}
		}
		return temp;
	}
	
	public String processLst2String(LinkedList<Node> lst) {
		int size = lst.size();
		if(size == 0)
			return "空过程";
		String str = "";
		for(int i=1;i<=size;i++) {
			str = str + "Step" + i + ": " + lst.get(i-1).getFro() + " -> " + lst.get(i-1).getArf() + "<br>";
		}
		return str;
	}
	
	public void printRB() {
		int size = RuleBase.size();
        for(int i=0;i<size;i++) {
        	System.out.println( i+1 + ": " + RuleBase.get(i).getFro() + " -> " + RuleBase.get(i).getArf() + "  level:" + RuleBase.get(i).getLevel());
        }
	}
	
	public void sortRB() {
		RuleBase.sort(new Comparator<Node>(){
			@Override
			public int compare(Node arg0, Node arg1) {
				return arg0.getFro().size()-arg1.getFro().size();
				// 前件数量升序排列
			}
		});
		
	}
	
	public boolean loadRB() {
//		String rootPath= System.getProperty("user.dir");
//		System.out.println(rootPath);

		File file = new File(".\\src\\rulebase.txt");
		// E:\Java\人工智能实验_基于产生式的动物识别专家系统\ExpertSystem\src\rulebase.txt
		try {
			BufferedReader br = new BufferedReader(new FileReader(file));
			String str;
	        while(true)
	        {
	        	str = br.readLine();
	        	if(str == null || str.equals(""))
	        		break;
	        	String fro = null;
	        	String arf = null;
	        	int len = str.length(), start = 0;
	        	// 查找前件
	        	for(int i=0;i<len;i++){
	        		if(str.charAt(i) == '-') {
	        			fro = str.substring(start, i);
	        			start = i;
	        			break;
	        		}
	        	}
	        	// 查找后件
	        	for(int i=start+2;i<len;i++) {
	        		if(str.charAt(i) == '_') {
	        			arf = str.substring(start+2, i);
	        			start = i;
	        			break;
	        		}
	        	}
	        	LinkedList<String> lst = this.froString2Lst(fro);
	        	int level = Integer.valueOf(str.substring(start+1, start+2));
	        	this.preInsertRB(lst, arf, level);
	        }
	        this.sortRB();
	        this.printRB();
			br.close();
		}catch(IOException e) {
			Msgbox msgbox = new Msgbox("<html>" + e.getMessage() + "<html>");
			System.out.println(e.getMessage());
			msgbox.run();
			return false;
		}
		return true;
	}
	
	public boolean saveRB(LinkedList<String> lst, String str, int level) {
		this.sortRB();
		File file = new File(".\\src\\rulebase.txt");
		try {
			BufferedWriter bw = new BufferedWriter(new FileWriter(file, true));
			int size = lst.size();
			for(int i=0;i<size-1;i++) {
				bw.append(lst.get(i) + "&");
			}
			bw.append(lst.get(size-1) + "->" + str + "_" + level);
			bw.newLine();
			bw.close();
		}catch(IOException e) {
			Msgbox msgbox = new Msgbox("<html>" + e.getMessage() + "<html>");
			System.out.println(e.getMessage());
			msgbox.run();
			return false;
		}
		return true;
	}
	
//	public void insertDB(String str) {
//		this.DataBase.add(str);
//	}
//	
//	public void insertRB(Node node) {
//		this.RuleBase.add(node);
//	}
//	
//	public void insertPC(Node node) {
//		this.Process.add(node);
//	}
	
	public void clearDB() {
		this.DataBase.clear();
	}
	
//	public void clearPC() {
//		this.Process.clear();
//	}
	
	public void resetRB() {
		int len = RuleBase.size();
		for(int i=0;i<len;i++) {
			RuleBase.get(i).setUse(false);
		}
	}
	
	public boolean existInDB(String str) {
		int len = DataBase.size();
		for(int i=0;i<len;i++) {
			if(DataBase.get(i).equals(str)) {
				return true;
			}
		}
		return false;
	}
	
	public boolean existInRB(Node node) {
		int len = RuleBase.size();
		for(int i=0;i<len;i++) {
			if(RuleBase.get(i).equal(node))
				return true;
		}
		return false;
	}
	
	public boolean preInsertDB(String str) {
		if(existInDB(str)) {
			System.out.println("DataBase: 存在重复元素!");
			return false;
		}else {
			DataBase.add(str);
			return true;
		}
	}
	
	public boolean preInsertRB(LinkedList<String> fro, String arf, int level) {
		Node newNode = new Node();
//		LinkedList<String> temp = new LinkedList<String>();
//		int len = fro.length();
//		int start = 0;
//		for(int i=0;i<len;i++) {
//			if(i == len-1) {
//				temp.add(fro.substring(start, i+1));
//				break;
//			}
//			if(fro.charAt(i) == '&') {
//				temp.add(fro.substring(start, i)); 
//				start = i+1;
//				i = i + 1;
//			}
//		}
		
		newNode.setFro(fro);
		newNode.setArf(arf);
		newNode.setLevel(level);
		newNode.setUse(false);
		
		if(!existInRB(newNode)) {
			this.RuleBase.add(newNode);
			return true;
		}else {
			System.out.println("RuleBase: 存在重复元素!");
			return false;
		}
		
	}
	
	// MAIN PROCESS 
	public boolean mainProcess() {
		LinkedList<Node> Process = new LinkedList<Node>();	// 保存推理过程
		boolean isInterrupt = false;	// 推理是否中断(未找到最终结论)
		boolean breakFlag = false;
		boolean ansInDB = false;
		String ans = "推理失败";
		String prcstr = null;		// 推理过程的字符串形式
		
		// 推导
		while(true) {
			if(breakFlag)
				break;
			
			int index = -1;
			boolean findNode = false;
			int RBsize = RuleBase.size();
			int DBsize = DataBase.size();
			// 先查询事实库中有没有结论,若有则提前终止
			for(int i=0;i<RBsize;i++) {
				for(int j=0;j<DBsize;j++) {
					if(DataBase.get(j).equals(RuleBase.get(i).getArf()) && RuleBase.get(i).getLevel() == 1) {
						ansInDB = true;
						ans = DataBase.get(j);
						break;
					}
				}
				if(ansInDB)
					break;
			}
			
			if(ansInDB) {
				prcstr = "事实库中存在结论:" + ans;
				Answer answer3 = new Answer(ans, prcstr, false);
				answer3.run();
//				this.resetRB();
				return true;
			}

			for(int i=0;i<RBsize;i++) {
				int froSize = RuleBase.get(i).getFro().size();
				int cnt = 0;
				for(int j=0;j<froSize;j++) {
					if(!RuleBase.get(i).isUse()) {
						for(int k=0;k<DBsize;k++) {
							if(DataBase.get(k).equals(RuleBase.get(i).getFro().get(j))) {
								cnt++;
								break;
							}
						}
					}
				}
				if(cnt == froSize) {	// 找到匹配的规则
					index = i;
					findNode = true;
					break;
//					if(!findNode) {
//						index = i;
//						findNode = true;
//					}	
//					else {	// 选择优先级更大(结论范围更小)的规则
//						index = (RuleBase.get(index).getLevel()>RuleBase.get(i).getLevel())?index:i;	
//					}
				}
			}
			// 一轮匹配结束
			if(index == -1) {
				breakFlag = true;
				isInterrupt = true;
			}else if(RuleBase.get(index).getLevel() == 1){
				breakFlag = true;
				ans = RuleBase.get(index).getArf();
//				RuleBase.get(index).setUse(true);
				Process.add(RuleBase.get(index));
				if(!this.existInDB(RuleBase.get(index).getArf()))
					DataBase.add(RuleBase.get(index).getArf());
			}else {
				RuleBase.get(index).setUse(true);
				Process.add(RuleBase.get(index));
				if(!this.existInDB(RuleBase.get(index).getArf()))
					DataBase.add(RuleBase.get(index).getArf());
			}
			
		}// 推导终止
		
		// process -> prcstr
		prcstr = this.processLst2String(Process);
		
		if(isInterrupt) {
			Answer answer1 = new Answer(ans, prcstr, true);
			answer1.run();
			this.resetRB();
			return false;
		}
		Answer answer2 = new Answer(ans, prcstr, false);
		answer2.run();
		this.resetRB();
		return true;
	}
	
}
1.2. Node.java
package solution;

import java.util.LinkedList;

public class Node{
	private LinkedList<String> fro = new LinkedList<String>();
//	private String fro;
	private String arf;
	private int level = -1;
	private boolean isUse = false;
	
	public Node() {
		
	}
	
	public Node(LinkedList<String> a, String b, int c, boolean d) {
//		this.fro = a;
		for(int i=0;i<a.size();i++) {
			this.fro.add(a.get(i));
		}
		this.arf = b;
		this.level = c;
		this.isUse = d;
	}
	
	public boolean equal(Node node) {
		if(this.fro.equals(node.getFro()) && this.arf.equals(node.getArf())) {
			return true;
		}
		return false;
	}

	public LinkedList<String> getFro() {
		return fro;
	}

	public void setFro(LinkedList<String> fro) {
//		this.fro = fro;
		for(int i=0;i<fro.size();i++) {
			this.fro.add(fro.get(i));
		}
	}

	public String getArf() {
		return arf;
	}

	public void setArf(String arf) {
		this.arf = arf;
	}

	public int getLevel() {
		return level;
	}

	public void setLevel(int level) {
		this.level = level;
	}

	public boolean isUse() {
		return isUse;
	}

	public void setUse(boolean isUse) {
		this.isUse = isUse;
	}
	
	
}

2. package window

2.1 Answer.java
package window;

import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JLabel;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Color;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.JButton;

public class Answer extends JFrame {

	private JPanel contentPane;

	/**
	 * Launch the application.
	 */
	public void run() {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
			this.setVisible(true);
			this.setAlwaysOnTop(true);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Create the frame.
	 */
	public Answer(String ans, String process, boolean flag) {
		setTitle("推理结果");
		setResizable(false);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 523, 353);
		contentPane = new JPanel();
		contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));

		setContentPane(contentPane);
		contentPane.setLayout(null);
		
		String str = "";
		if(flag)
			str = "推理中断,请补充事实";
		JLabel label = new JLabel("<html>\r\n<body>\r\n    <h1 style=\"font-size: 16px;\"><strong >推理结果: <span style=\"color: red;\">"+ ans +"</span></strong></h1>\r\n    <p style=\"font-size: 12px;\">\r\n        推理过程:<br>\r\n        " + process + "\r\n    </p>\r\n    <br>\r\n    <strong style=\"font-size: 12px;\">" + str + "</strong>\r\n</body>\r\n</html>");
		label.setVerticalAlignment(SwingConstants.TOP);
		label.setFont(new Font("微软雅黑", Font.PLAIN, 14));
		label.setBounds(55, 10, 419, 247);
		contentPane.add(label);
		
		JButton button = new JButton("确认");
		button.setFont(new Font("微软雅黑", Font.PLAIN, 13));
		button.setBounds(212, 267, 97, 23);
		contentPane.add(button);
		
		button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				dispose();
			}
		});
	}
}
2.2 Interaction.java
package window;

import solution.Infer;
import solution.Node;

import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.JLabel;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;

import javax.swing.JButton;
import javax.swing.SwingConstants;
import javax.swing.JTextField;
import javax.swing.JTextArea;
import javax.swing.ButtonGroup;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;


public class Interaction extends JFrame {

	private JPanel menupanel;
	private JTextField textField_DB;
	private JTextField textField_RB_FRO;
	private JTextField textField_RB_ARF;
	
	private Infer infer = new Infer();

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		String laf = UIManager.getSystemLookAndFeelClassName();
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					UIManager.setLookAndFeel(laf);
					Interaction frame = new Interaction();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public Interaction() {
		infer.loadRB();		
		
		setResizable(false);
		setTitle("基于产生式的动物识别专家系统");
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setBounds(100, 100, 848, 598);
		menupanel = new JPanel();
		menupanel.setBorder(new EmptyBorder(5, 5, 5, 5));

		setContentPane(menupanel);
		menupanel.setLayout(null);
		
		JLabel titleLabel1 = new JLabel("欢迎使用");
		titleLabel1.setFont(new Font("微软雅黑", Font.ITALIC, 19));
		titleLabel1.setBounds(221, 10, 112, 34);
		menupanel.add(titleLabel1);
		
		JLabel titleLabel2 = new JLabel("基于产生式的动物识别专家系统");
		titleLabel2.setFont(new Font("微软雅黑", Font.ITALIC, 19));
		titleLabel2.setBounds(343, 33, 290, 34);
		menupanel.add(titleLabel2);
		
		JLabel contentLabel1 = new JLabel("综合数据库");
		contentLabel1.setHorizontalAlignment(SwingConstants.CENTER);
		contentLabel1.setFont(new Font("微软雅黑", Font.PLAIN, 14));
		contentLabel1.setBounds(58, 81, 118, 26);
		menupanel.add(contentLabel1);
		
		textField_DB = new JTextField();
		textField_DB.setToolTipText("");
		textField_DB.setHorizontalAlignment(SwingConstants.LEFT);
		textField_DB.setFont(new Font("微软雅黑", Font.PLAIN, 13));
		textField_DB.setBounds(229, 117, 274, 26);
		menupanel.add(textField_DB);
		textField_DB.setColumns(10);
		
		JLabel contentLabel2 = new JLabel("输入事实");
		contentLabel2.setHorizontalAlignment(SwingConstants.CENTER);
		contentLabel2.setFont(new Font("微软雅黑", Font.PLAIN, 14));
		contentLabel2.setBounds(356, 81, 118, 26);
		menupanel.add(contentLabel2);
		
		JTextArea textArea_DB = new JTextArea();
		textArea_DB.setFont(new Font("微软雅黑", Font.PLAIN, 13));
		textArea_DB.setEditable(false);
//		textArea_DB.setBounds(31, 117, 173, 413);
		menupanel.add(textArea_DB);
		
		JScrollPane scrollPane_DB = new JScrollPane(textArea_DB);
		scrollPane_DB.setBounds(31, 117, 173, 413);
		menupanel.add(scrollPane_DB);
		
		JTextArea textArea_RB = new JTextArea();
		textArea_RB.setFont(new Font("微软雅黑", Font.PLAIN, 13));
		textArea_RB.setEditable(false);
//		textArea_RB.setBounds(639, 117, 173, 413);
		menupanel.add(textArea_RB);
		
		JScrollPane scrollPane_RB = new JScrollPane(textArea_RB);
		scrollPane_RB.setBounds(639, 117, 173,413);
		menupanel.add(scrollPane_RB);
		
		// 向 textArea_RB 插入已有规则
		int size = infer.getRBsize();
		for(int i=0;i<size;i++)
			textArea_RB.append(infer.getRuleBase(i));
		
		JLabel contentLabel3 = new JLabel("规则库");
		contentLabel3.setHorizontalAlignment(SwingConstants.CENTER);
		contentLabel3.setFont(new Font("微软雅黑", Font.PLAIN, 14));
		contentLabel3.setBounds(665, 81, 118, 26);
		menupanel.add(contentLabel3);
		
		JButton button1 = new JButton("输入");
		button1.setFont(new Font("微软雅黑", Font.PLAIN, 13));
		button1.setBounds(526, 119, 97, 23);
		menupanel.add(button1);
		
		button1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				String str = textField_DB.getText();
				if(str.equals("")) {
					System.out.println("输入不能为空");
					return;
				}
				if(infer.preInsertDB(str))
					textArea_DB.append(str + '\n');
				else {
					// remind
					Msgbox msgbox = new Msgbox("DataBase: 存在重复元素!");
					msgbox.run();
				}
			}
		});
		
		JLabel contentLabel5 = new JLabel("输入规则");
		contentLabel5.setHorizontalAlignment(SwingConstants.CENTER);
		contentLabel5.setFont(new Font("微软雅黑", Font.PLAIN, 14));
		contentLabel5.setBounds(356, 265, 118, 26);
		menupanel.add(contentLabel5);
		
		JLabel contentLabel4 = new JLabel("<html>\r\n输入要求:\r\n<ol>\r\n<li>为确保正确推理,请勿在输入事实时更改规则库</li>\r\n<li>依次输入多条事实或规则</li>\r\n<li>规则前件中的每个前提用\"&\"分隔</li>\r\n<li>输入规则后件时必须选择类型</li>\r\n</ol>\r\n</html>");
		contentLabel4.setHorizontalAlignment(SwingConstants.CENTER);
		contentLabel4.setFont(new Font("微软雅黑", Font.ITALIC, 13));
		contentLabel4.setBounds(239, 153, 390, 114);
		menupanel.add(contentLabel4);
		
		textField_RB_FRO = new JTextField();
		textField_RB_FRO.setToolTipText("");
		textField_RB_FRO.setHorizontalAlignment(SwingConstants.LEFT);
		textField_RB_FRO.setFont(new Font("微软雅黑", Font.PLAIN, 13));
		textField_RB_FRO.setColumns(10);
		textField_RB_FRO.setBounds(295, 301, 316, 26);
		menupanel.add(textField_RB_FRO);
		
		JRadioButton radioButton0 = new JRadioButton("否");
		radioButton0.setSelected(true);
		radioButton0.setVerticalAlignment(SwingConstants.BOTTOM);
		radioButton0.setFont(new Font("微软雅黑", Font.PLAIN, 13));
		radioButton0.setBounds(526, 402, 56, 23);
		menupanel.add(radioButton0);
		
		JRadioButton radioButton1 = new JRadioButton("是");
		radioButton1.setVerticalAlignment(SwingConstants.BOTTOM);
		radioButton1.setFont(new Font("微软雅黑", Font.PLAIN, 13));
		radioButton1.setBounds(526, 377, 56, 23);
		menupanel.add(radioButton1);
		
		ButtonGroup bg = new ButtonGroup();
		bg.add(radioButton0);
		bg.add(radioButton1);
		
		JButton button2 = new JButton("输入并保存");
		button2.setFont(new Font("微软雅黑", Font.PLAIN, 13));
		button2.setBounds(526, 507, 97, 23);
		menupanel.add(button2);
		
		button2.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				String Fro = textField_RB_FRO.getText();
				String Afr = textField_RB_ARF.getText();
				int level = -1;
				if(Fro.equals("") || Afr.equals("") || bg.getSelection() == null){
					System.out.println("输入不能为空");
					return;
				}
				if(radioButton1.isSelected())
					level = 1;
				else if(radioButton0.isSelected())
					level = 0;
				
				LinkedList<String> Lst = infer.froString2Lst(Fro);
				if(infer.preInsertRB(Lst, Afr, level)) {
//					textArea_RB.append(Lst + "->" + Afr + " ("+ level + ")\n");
					// save in file
					infer.saveRB(Lst, Afr, level);
					// reload data
					textArea_RB.selectAll();
					textArea_RB.replaceSelection("");
					int size = infer.getRBsize();
					for(int i=0;i<size;i++) {
						textArea_RB.append(infer.getRuleBase(i));
					}
				}
				else {
					// remind
					Msgbox msgbox = new Msgbox("RuleBase: 存在重复元素!");
					msgbox.run();
				}
			}
		});
		
		JLabel contentLabel6 = new JLabel("<html>\r\n规则后件类型:<br>\r\n是否为最终结论(默认为否)\r\n</html>");
		contentLabel6.setHorizontalAlignment(SwingConstants.CENTER);
		contentLabel6.setFont(new Font("微软雅黑", Font.PLAIN, 14));
		contentLabel6.setBounds(289, 377, 214, 46);
		menupanel.add(contentLabel6);
		
		JLabel contentLabel7 = new JLabel("IF");
		contentLabel7.setHorizontalAlignment(SwingConstants.CENTER);
		contentLabel7.setFont(new Font("微软雅黑", Font.PLAIN, 14));
		contentLabel7.setBounds(229, 300, 56, 26);
		menupanel.add(contentLabel7);
		
		JLabel contentLabel8 = new JLabel("THEN");
		contentLabel8.setHorizontalAlignment(SwingConstants.CENTER);
		contentLabel8.setFont(new Font("微软雅黑", Font.PLAIN, 14));
		contentLabel8.setBounds(229, 337, 54, 26);
		menupanel.add(contentLabel8);
		
		textField_RB_ARF = new JTextField();
		textField_RB_ARF.setToolTipText("");
		textField_RB_ARF.setHorizontalAlignment(SwingConstants.LEFT);
		textField_RB_ARF.setFont(new Font("微软雅黑", Font.PLAIN, 13));
		textField_RB_ARF.setColumns(10);
		textField_RB_ARF.setBounds(295, 337, 316, 26);
		menupanel.add(textField_RB_ARF);
		
		JLabel contentLabel9 = new JLabel("<html>\r\n其他提示:\r\n<ul>\r\n<li>规则库文件保存在rulebase.txt中</li>\r\n<li>规则最后的数字代表是否为最终结论</li>\r\n</ul>\r\n</html>");
		contentLabel9.setHorizontalAlignment(SwingConstants.CENTER);
		contentLabel9.setFont(new Font("微软雅黑", Font.ITALIC, 13));
		contentLabel9.setBounds(239, 424, 325, 91);
		menupanel.add(contentLabel9);
		
		JButton button3 = new JButton("开始推理");
		button3.setFont(new Font("微软雅黑", Font.PLAIN, 13));
		button3.setBounds(675, 42, 97, 23);
		menupanel.add(button3);
		
		button3.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				infer.mainProcess();
				int DBsize = infer.getDBsize();
				textArea_DB.selectAll();
				textArea_DB.replaceSelection("");
				for(int i=0;i<DBsize;i++) {
					textArea_DB.append(infer.getDataBase(i) + "\n");
				}
			}
		});
		
		JButton button4 = new JButton("清空数据库");
		button4.setFont(new Font("微软雅黑", Font.PLAIN, 13));
		button4.setBounds(65, 42, 97, 23);
		menupanel.add(button4);
		
		button4.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				infer.clearDB();
				textArea_DB.selectAll();
				textArea_DB.replaceSelection("");
			}
		});
	}
}
2.3 Msgbox.java
package window;

import java.awt.BorderLayout;
import java.awt.FlowLayout;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.JLabel;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.SwingConstants;

public class Msgbox extends JDialog {

	private final JPanel contentPanel = new JPanel();

	/**
	 * Launch the application.
	 */
	public void run() {
		String laf = UIManager.getSystemLookAndFeelClassName();
		try {
			UIManager.setLookAndFeel(laf);
//			Msgbox dialog = new Msgbox("null");
			this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			this.setVisible(true);
			this.setAlwaysOnTop(true);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Create the dialog.
	 */
	public Msgbox(String msg) {
		setTitle("提示");
		setBounds(100, 100, 459, 193);
		getContentPane().setLayout(new BorderLayout());
		contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
		getContentPane().add(contentPanel, BorderLayout.CENTER);
		contentPanel.setLayout(null);
		{
			JLabel label = new JLabel(msg);
			label.setHorizontalAlignment(SwingConstants.CENTER);
			label.setBounds(71, 10, 315, 71);
			label.setFont(new Font("微软雅黑", Font.BOLD, 14));
			contentPanel.add(label);
		}
		{
			JPanel buttonPane = new JPanel();
			buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
			getContentPane().add(buttonPane, BorderLayout.SOUTH);
			{
				JButton okButton = new JButton("确认");
				okButton.setFont(new Font("微软雅黑", Font.PLAIN, 13));
				okButton.setActionCommand("OK");
				buttonPane.add(okButton);
				getRootPane().setDefaultButton(okButton);
				
				okButton.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent arg0) {
						dispose();
					}
				});
			}
		}
	}
}

  • 6
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值