基本程序分析:
1.加载事实信息和规则信息
XmlDocument rules = new XmlDocument();
string directory = AppDomain.CurrentDomain.BaseDirectory + @"\..\..\..\RuleExample.xml";//规则链条
rules.Load(directory);
ROM rom = Compiler.Compile(rules);//加载,并确认 事实与规则之间的关系
每个链表规则的对应的Fact,都存入DependentEvidence List中。
2. 加载需要的参数,也可以说是激活的一种方式。
//模型
XmlDocument model = new XmlDocument();
model.LoadXml("<a><number1>10</number1><number2>1</number2><result2/></a>");
rom.AddModel("bob", model);
这种激活方式,是不需要匹配的,而是把所有的链表规则统统执行一遍, 测试例子上是因为在此数据时事实的参数,而链表规则的条件是此事实的参数的计算而得到的真假。
<Rule id="Rule1" desc="" chainable="True"> <Condition><![CDATA[ NOT(F2>F1) ]]></Condition>
<Fact id="F1" desc="1" type="double" modelId="bob">
<xpath><![CDATA[ a/number1 ]]></xpath>
</Fact>
3.引起全部规则计算
rom.Evaluate();
调用Decisions类的.Evaluate()方法。
通过上面11个图。可以看出具体的加载之后的数据的存储,数据类型与数据之间的关系了。
4.核心的推理函数
public void Evaluate(Dictionary<string, IEvidence> evidenceCollection, Dictionary<string, List<string>> factRelationships) { #region register all evidences in this rom with this instance of decision foreach (IEvidence evidence in evidenceCollection.Values) { evidence.CallbackLookup += RaiseCallback; evidence.EvidenceLookup += RaiseEvidenceLookup; evidence.ModelLookup += RaiseModelLookup; evidence.Changed += delegate(object sender, ChangedArgs args) { IEvidence evidence1 = (IEvidence)sender; if (!(evidence1 is IFact)) return; //找出这ifact模型 IFact fact = (IFact)evidence1; IEvidenceValue value = (IEvidenceValue)fact.ValueObject; string modelId = value.ModelId; //遍历所有ifacts并添加这些到相同的模型来执行列表 foreach(IEvidence evidence2 in evidenceCollection.Values) { //排除所有不IFact证据类型 if (!(evidence2 is IFact)) continue; //排除自己 if (evidence2.ID == evidence1.ID) continue; //排除所有那些不同的ifacts模型 if (evidence2.ValueObject.ModelId != modelId) continue; //加入list executionList.Add(evidence2); } }; } #endregion #region load up the execution list with facts //加载执行列表与事实 foreach (IEvidence fact in evidenceCollection.Values) { if (!(fact is IFact)) continue; executionList.Add(fact); Debug.WriteLine("Added fact to execution list: " + fact.ID); } #endregion #region load up the execution list with chainable rules //加载执行列表链式规则 foreach (IEvidence rule in evidenceCollection.Values) { if (rule is IRule && ((IRule)rule).isChainable) { executionList.Add(rule); Debug.WriteLine("Added rule to execution list: " + rule.ID); } } #endregion #region execute list //执行列 //Debug.WriteLine("Iteration"); //Debug.IndentLevel++; while (executionList.HasNext) { Trace.WriteLine("Execution List: " + executionList.ToString()); Trace.WriteLine("Processing"); //Debug.IndentLevel++; //最低优先级作为计算List第一项。 string evidenceId = executionList.Read(); IEvidence evidence = evidenceCollection[evidenceId]; Trace.WriteLine("EvidenceId: " + evidence.ID); //计算证明 evidence.Evaluate(); //如果证明有子句的,增加它的行为 if (evidence.ClauseEvidence!=null) { foreach (string clauseEvidenceId in evidence.ClauseEvidence) { Evidence.IEvidence clauseEvidence = (Evidence.IEvidence)evidenceCollection[clauseEvidenceId]; executionList.Add(clauseEvidence); Trace.WriteLine("增加证据执行列表: " + clauseEvidence.ID); } } //执行列表的链式的相关的事实 if (factRelationships.ContainsKey(evidence.ID)) { List<string> dependentFacts = factRelationships[evidence.ID]; foreach (string dependentFact in dependentFacts) { Evidence.IEvidence dependentEvidence = (Evidence.IEvidence)evidenceCollection[dependentFact]; executionList.Add(dependentEvidence); Trace.WriteLine("加入链式的相关事实到执行列表: " + dependentEvidence.ID); } } //Debug.IndentLevel--; Trace.WriteLine("End Processing"); Trace.WriteLine(""); } //Debug.IndentLevel--; Trace.WriteLine("End Iteration"); #endregion } |
//加载执行列表与事实
//加载执行列表链式规则
while (executionList.HasNext)//这里是核心的推理。
大家也看到了 发布的推理中间结果输出有点混乱。我会专门搞一篇,把中间结果的推理过程写出来的。
从 rule1的 execute 推到 rule2
利用嵌套方式来 得到的。 但是推理过程并不是那么明晰。
F1。 删除F1
不是fact 继续, 是fact 下一步。 ID一样, 继续, 不一样下一步,
是fact了。 然后ID不同了。 model是一个组。 加入。 F2 true false 此ID 若以存在则返回, 若不存在,则加入。--
计算完成。
找到 其action 找到是否是别的链条的表达式的值。 F1、F2是 rule1的。 条件中的。 把rule1 加入到 执行列表。 此ID 若以存在则返回, 若不存在,则加入。--
result了
得到XML的值,false
则F1没有。 则插入F1 可估算 因为插入后成 True, F2, False, Rule3, Rule1,F1
然后排序。 排成
False, F1, True, F2, Rule3, Rule1
然后随着走。 走到。Rule3, Rule1
以Rule1来看。
规则的Evaluate()
这里可以看出, 表达式,事实,规则 都有自己的解析程序。
计算规则的后缀表达式的值,得到结果。
Rule1 有两个处理语句。是行为处理表达式。
把表达式加入 LIST
Rule1-Rule2-1, Rule1-F2-0
执行表达式Rule1-Rule2-1
行为处理表达式的 执行, 下一条的规则是Rule2
rule2加入规则。
List变成 Rule1-F2-0, Rule2
然后执行Rule1-F2-0。 操作fact的表达式。
这个是一个表达式。 F2+1。 计算表达式,得到值,返回。
并赋值给 Fact, 输出。
此表达式引发事实改变, 事实是F2. 把F2加入List
加入时,因为F2已经不在其中。所以把F2加入时,引发所有的事实都加入了。
因为在计算时。 IFact fact = RaiseEvidenceLookup(this, new EvidenceLookupArgs(operatingId)) as IFact; 会因为F2的方法。
触发了绑定的Changed方法。
List变成 F1, F2, False, True, Result,Rule2
ExpressionEvaluator FACT F2 = 1 ExpressionEvaluator 1 + 1 = 2
然后再次计算。
最后得到 ExpressionEvaluator FACT F2 = 2 ExpressionEvaluator FACT F1 = 10 ExpressionEvaluator 2 > 10 = False ExpressionEvaluator NOT False = True |
大嘴巴一下:
这种推理引擎把事实作为一个可以操作的数据处理,对表达式的处理更是非常棒,
我必须要根据这个推理引擎,重新设计一下我的专家系统的了。
本人声明:沐海(http://my.oschina.net/mahaisong) 以上文章是经过本人设计实践和阅读其他文档得出。如果需要探讨或指教可以留言!欢迎