由于研究生阶段的需要,需要系统学习C#语言,为了将最近学习的东西进行一个总结,偶然间想到了本科写的那个动物识别系统,用C语言写的很简单,只是用了简单的if…else语句,这里也是简单的用C#语言进行了一个复现。原来的C语言版本在这里:动物识别系统C语言版
有如下规则库,用户来选择这些条件,根据用户输入的条件来判断这是一种什么动物:
编号 | IF条件 | 推论 |
---|---|---|
R1 | 有毛 | 哺乳动物 |
R2 | 有奶 | 哺乳动物 |
R3 | 有羽毛 | 鸟 |
R4 | 会飞 & 会下蛋 | 鸟 |
R5 | 吃肉 | 食肉动物 |
R6 | 有犬齿 & 有爪 & 眼盯前方 | 食肉动物 |
R7 | 哺乳动物 & 有蹄 | 有蹄类动物 |
R8 | 哺乳动物 & 反刍动物 | 有蹄类动物 |
R9 | 哺乳动物 & 食肉动物 & 黄褐色 & 暗斑点 | 金钱豹 |
R10 | 哺乳动物 & 食肉动物 & 黄褐色 & 黑色条纹 | 虎 |
R11 | 有蹄类动物 & 长脖子 & 长腿 & 暗斑点 | 长颈鹿 |
R12 | 有蹄类动物 & 黑色条纹 | 斑马 |
R13 | 鸟 & 不会飞 & 长脖子 & 长腿 & 黑白二色 | 鸵鸟 |
R14 | 鸟 & 不会飞 & 会游泳 & 黑白二色 | 企鹅 |
R15 | 鸟 & 善飞 | 信天翁 |
如果用一个图来表示就是这样的:
图中,食肉动物那一列,需要同时满足时哺乳动物才能继续往下推出时金钱豹或老虎。
我采用的是字典作为知识库和规则库的存储结构,知识库的结构是:
Dictionary<int, string> _knowledgeBase = new Dictionary<int, string>()
{
{1, "有毛发"},{2, "有奶 "},{3 ,"有羽毛"},{4 ,"会飞 "},
{5 ,"会下蛋"},{6 ,"眼盯前方"},{7 ,"有蹄 "},{8 ,"反刍动物"},
{9 ,"吃肉 "},{10 ,"有犬齿"},{11 ,"有爪"},{12 ,"长脖子"},
{13 ,"长腿"},{14 ,"有暗斑点"},{15 ,"有黑色条纹"},{16 ,"黄褐色"},
{17 ,"不会飞"},{18 ,"黑白二色"},{19 ,"会游泳"},{20 ,"擅飞"}
};
这样知识库条件的序号就一一对应上了,也方便输出。
规则库的结构是:
Dictionary<int[], string> _ruleBase = new Dictionary<int[], string>();
之所以选择int数组作为其存储结构,在用户输入后,把用户输入的条件序号转换为一个int数组,这样就可以直接看字典中是否有这个键,当然要对这个int数组进行一个约束,因为对于判断数组相同的要求是:1.数组元素个数相同。2.对应位置上的元素相同,我这里采用的方式是对int数组进行一个排序,即使用户不是按照顺序输入的条件,也能进行判断。
遇到的问题:因为int数组是引用类型,存储的是地址,因次即使Dictionary中存在和用户输入相同的键,也不能进行判断,用户输入的条件数组的地址和Dictionary中键的地址是不一样的,比如:
Dictionary<int[], string> _rule = new Dictionary<int[], string>();
_rule.Add(new int[] { 1, 2, 3 }, "鸟");
int[] rule = { 1, 2, 3 };
bool result = _rule.ContainsKey(rule); //result的结果为false
result的结果居然是false,那就是引用类型的缘故,解决方法:需要重写Equals()方法,以及重写GetHashCode()方法。
参考自文章:C#中,对于Dictionary字典类型,使用引用类型做Key的注意事项
using System;
using System.Collections.Generic;
using System.Linq;
namespace AnimalSystem
{
// 使用引用类型作为字典的Key,需要重写Equals方法和GetHashCode()方法
public class ArrayExtend
{
int[] arr;
string content = "";
public void Init(int[] a)
{
int count = a.Count();
arr = new int[count];
for (int i = 0; i < count; i++)
{
arr[i] = a[i];
content += arr[i] + ",";
}
}
public override bool Equals(object obj)
{
ArrayExtend temp22 = (ArrayExtend)obj;
bool flag = true;
for (int i = 0; i < arr.Count(); i++)
{
if (arr[i] != temp22.arr[i])
flag = false;
}
return flag;
}
public override int GetHashCode()
{
return content.GetHashCode();
}
}
class Program
{
static void Main(string[] args)
{
// 准备知识库
Dictionary<int, string> _knowledgeBase = new Dictionary<int, string>()
{
{1, "有毛发"},{2, "有奶 "},{3 ,"有羽毛"},{4 ,"会飞 "},
{5 ,"会下蛋"},{6 ,"眼盯前方"},{7 ,"有蹄 "},{8 ,"反刍动物"},
{9 ,"吃肉 "},{10 ,"有犬齿"},{11 ,"有爪"},{12 ,"长脖子"},
{13 ,"长腿"},{14 ,"有暗斑点"},{15 ,"有黑色条纹"},{16 ,"黄褐色"},
{17 ,"不会飞"},{18 ,"黑白二色"},{19 ,"会游泳"},{20 ,"擅飞"}
};
// 这里使用的是int[]数组类型作为字典的Key,其使用方法引用自文章
// https://www.cnblogs.com/WangSong-Gemini/p/16407833.html
// 准备规则库(命名规则:第一个数字代表动物编号,第二个数字代表该动物的条件编号)
Dictionary<ArrayExtend, string> _ruleBase = new Dictionary<ArrayExtend, string>();
ArrayExtend a11 = new ArrayExtend();
ArrayExtend a12 = new ArrayExtend();
ArrayExtend a13 = new ArrayExtend();
ArrayExtend a14 = new ArrayExtend();
ArrayExtend a21 = new ArrayExtend();
ArrayExtend a22 = new ArrayExtend();
ArrayExtend a23 = new ArrayExtend();
ArrayExtend a24 = new ArrayExtend();
ArrayExtend a31 = new ArrayExtend();
ArrayExtend a32 = new ArrayExtend();
ArrayExtend a33 = new ArrayExtend();
ArrayExtend a34 = new ArrayExtend();
ArrayExtend a41 = new ArrayExtend();
ArrayExtend a42 = new ArrayExtend();
ArrayExtend a43 = new ArrayExtend();
ArrayExtend a44 = new ArrayExtend();
ArrayExtend a51 = new ArrayExtend();
ArrayExtend a52 = new ArrayExtend();
ArrayExtend a61 = new ArrayExtend();
ArrayExtend a62 = new ArrayExtend();
ArrayExtend a71 = new ArrayExtend();
ArrayExtend a72 = new ArrayExtend();
a11.Init(new int[] { 1, 7, 15 });
a12.Init(new int[] { 2, 7, 15 });
a13.Init(new int[] { 1, 8, 15 });
a14.Init(new int[] { 2, 8, 15 }); // 斑马
a21.Init(new int[] { 1, 7, 12, 13, 14 });
a22.Init(new int[] { 2, 7, 12, 13, 14 });
a23.Init(new int[] { 1, 8, 12, 13, 14 });
a24.Init(new int[] { 2, 8, 12, 13, 14 }); // 长颈鹿
a31.Init(new int[] { 1, 9, 14, 16 });
a32.Init(new int[] { 2, 9, 14, 16 });
a33.Init(new int[] { 1, 6, 10, 11, 14, 16 });
a34.Init(new int[] { 2, 6, 10, 11, 14, 16 }); // 金钱豹
a41.Init(new int[] { 1, 9, 15, 16 });
a42.Init(new int[] { 2, 9, 15, 16 });
a43.Init(new int[] { 1, 6, 10, 11, 15, 16 });
a44.Init(new int[] { 2, 6, 10, 11, 15, 16 }); // 虎
a51.Init(new int[] { 3, 12, 13, 17, 18 });
a52.Init(new int[] { 4, 5, 12, 13, 17, 18 }); // 鸵鸟
a61.Init(new int[] { 3, 20 });
a62.Init(new int[] { 4, 5, 20 }); // 信天翁
a71.Init(new int[] { 3, 17, 18, 19 });
a72.Init(new int[] { 4, 5, 17, 18, 19 }); // 企鹅
_ruleBase.Add(a11, "斑马");
_ruleBase.Add(a12, "斑马");
_ruleBase.Add(a13, "斑马");
_ruleBase.Add(a14, "斑马");
_ruleBase.Add(a21, "长颈鹿");
_ruleBase.Add(a22, "长颈鹿");
_ruleBase.Add(a23, "长颈鹿");
_ruleBase.Add(a24, "长颈鹿");
_ruleBase.Add(a31, "金钱豹");
_ruleBase.Add(a32, "金钱豹");
_ruleBase.Add(a33, "金钱豹");
_ruleBase.Add(a34, "金钱豹");
_ruleBase.Add(a41, "虎");
_ruleBase.Add(a42, "虎");
_ruleBase.Add(a43, "虎");
_ruleBase.Add(a44, "虎");
_ruleBase.Add(a51, "鸵鸟");
_ruleBase.Add(a52, "鸵鸟");
_ruleBase.Add(a61, "信天翁");
_ruleBase.Add(a62, "信天翁");
_ruleBase.Add(a71, "企鹅");
_ruleBase.Add(a72, "企鹅");
// 输出知识库
Console.WriteLine("===============================动物识别系统规则库===============================");
foreach (var item in _knowledgeBase)
{
Console.Write("{0}:{1}\t", item.Key, item.Value);
if (item.Key % 5 == 0) // 控制换行的逻辑
{
Console.WriteLine();
}
}
// 获取用户输入
Console.WriteLine("================================================================================");
Console.WriteLine("请输入已知条件(用空格分开),按回车结束:");
// 获取用户输入
string userInput = Console.ReadLine();
// 拆分用户输入
string[] inputx = userInput.Split(' ');
// 将用户输入的字符串转换成int数组
int[] inputy = new int[inputx.Length];
for (int i = 0; i < inputy.Length; i++)
{
inputy[i] = Convert.ToInt32(inputx[i]);
}
// 反馈给用户检查
Console.WriteLine("================================================================================");
Console.Write("请检查您的输入:");
foreach(var item in inputy)
{
Console.Write(item + " ");
}
Console.WriteLine();
Console.Write("Y/N?: ");
string inputCheck = Console.ReadLine();
if(inputCheck.Equals("Y") || inputCheck.Equals("y"))
{
Console.WriteLine("OK");
// 将用户的输入排个序
SortArray(inputy);
}
else if(inputCheck.Equals("N") || inputCheck.Equals("n"))
{
// 实现跳转到重新输入的逻辑?(待实现)
Console.WriteLine("Error");
}
else
{
Console.WriteLine("请重新输入:");
throw new Exception("输入错误");
// 跳转到重新输入的逻辑?
}
Console.WriteLine("================================================================================");
// 进行判断的逻辑
ArrayExtend inputUser = new ArrayExtend();
inputUser.Init(inputy);
try
{
// 如果当前规则库中没有存在这个规则在这里就会抛出异常
string result = _ruleBase[inputUser];
Console.WriteLine("根据您输入的条件,可以推断出这个动物是:" + result);
}
catch (Exception)
{
Console.WriteLine("根据您的输入条件,无法判断这是一种什么动物。");
}
Console.ReadLine();
}
// 将数组元素从小到大排序(用户的输入可能不是按照从小到大的顺序,根据规则库的存储结构,需要将用户的输入进行从小到大的排序)
static void SortArray(int[] arr)
{
for (int i = 0; i < arr.Length-1; i++)
{
for (int j = i + 1; j < arr.Length; j++)
{
if (arr[j] < arr[i])
{
int temp = arr[j];
arr[j] = arr[i];
arr[i] = temp;
}
}
}
}
}
}
代码还有很多不完善的地方,比如说实现反复推理,不是每次运行推理结束后又要点一次运行,又比如说在不小心输入错误了,实现重新输入等等。