Java中与Jess交互的全面指南
1. 处理Fact对象
1.1 多插槽(Multislots)
Jess事实可以包含多插槽,即单个插槽可容纳多个数据项。可以使用
addMultiSlot
方法向
Deftemplate
添加多插槽,然后像往常一样设置事实中该插槽的值,但插槽值必须是
RU.LIST
类型的
jess.Value
。
LIST
值包含一个
ValueVector
,
ValueVector
的元素就是多插槽的内容。示例代码如下:
Rete engine = new Rete();
Deftemplate d =
new Deftemplate("student", "A student", engine);
d.addSlot("name", Funcall.NIL, "STRING");
d.addMultiSlot("courses", Funcall.NILLIST);
engine.addDeftemplate(d);
Fact f = new Fact("student", engine);
f.setSlotValue("name", new Value("Fred Smith", RU.STRING));
ValueVector courses = new ValueVector();
courses.add(new Value("COMP 101", RU.STRING));
courses.add(new Value("HISTORY 202", RU.STRING));
f.setSlotValue("courses", new Value(courses, RU.LIST));
需要注意的是,即使多插槽只包含一个值,也必须将其放入
ValueVector
中。
1.2 有序事实(Ordered facts)
有序事实,如
(shopping-list bread milk jam)
,在内部表示为带有名为
__data
的单个多插槽的无序事实,即
(shopping-list (__data bread milk jam))
。如果从Jess语言断言有序事实,若模板不存在,会自动创建。从Java创建时,在调用
Fact
对象的构造函数时会创建模板。示例代码如下:
Fact f = new Fact("shopping-list", engine);
f.setSlotValue("__data", new Value(new ValueVector().
add(new Value("bread", RU.ATOM)).
add(new Value("milk", RU.ATOM)).
add(new Value("jam", RU.ATOM)), RU.LIST));
engine.assertFact(f);
这里
ValueVector
的
add
方法返回调用它的
ValueVector
,因此可以链式调用。
1.3 删除事实
可以使用
Rete
类的
retract
方法从工作内存中删除事实:
Fact f = …
engine.retract(f);
如果从Java断言事实,使用它作为
retract
的参数很容易。若不是,则有两种方法获取
Fact
对象:一是使用
Rete
的
findFactById
方法,以事实编号为参数;二是构造一个与要删除的事实相同的
Fact
对象。工作内存以哈希表形式存储,以
Fact
对象为键,查找事实是否存在很快,但按编号查找较慢。所以,如果从Java断言事实并打算后续删除,要保留对创建的
Fact
对象的引用。
2. 处理JavaBeans
从Java将JavaBeans添加到Jess的工作内存很简单,与从Jess语言操作类似。
Rete
类的方法与Jess函数的对应关系如下表所示:
| Rete方法 | Jess等效函数 |
| — | — |
| defclass(tag, class-name, parent) | (defclass tag class-name [parent]) |
| definstance(tag, object, boolean) | (definstance tag object [static|dynamic]) |
| undefinstance(object) | (undefinstance object) |
示例代码展示了如何将
java.awt.Button
添加到Jess的工作内存:
Rete engine = ...
engine.defclass("button", "java.awt.Button", null);
Button b = new Button("OK");
engine.definstance("button", b, true);
3. 从Java调用Jess函数
Rete
类将许多Jess函数公开为公共Java方法,但并非全部。有些Jess函数在其他类中以Java方法形式出现,如
Jess gensym*
函数可通过
jess.RU
类的静态
gensym
函数调用。其他Jess函数直接在
Userfunction
类中实现,可使用
jess.Funcall
类调用。例如,要在Java代码中开启所有监视诊断(相当于Jess中的
(watch all)
),可以这样做:
Rete engine = …
Context context = engine.getGlobalContext();
Funcall f = new Funcall("watch", engine);
f.arg("all");
f.execute(context);
也可以使用压缩形式:
new Funcall("watch", engine).arg("all").execute(context);
4. 处理JessException
Jess的Java API通过抛出
jess.JessException
实例来报告错误,因此在Java中使用Jess时,需要捕获此异常。
jess.JessException
有一些有用的方法:
-
getContext()
:返回Jess堆栈跟踪
-
getLineNumber()
:返回Jess语言代码中出错的行号
-
getNextException()
:返回嵌套异常
-
getProgramText()
:返回出错的Jess代码本身
-
getRoutine()
:返回Jess函数或Java方法的名称
示例代码展示了如何捕获和处理
JessException
:
import jess.*;
public class CatchJessException {
public static void main(String[] argv) {
try {
Rete r = new Rete();
r.executeCommand("(* 1 2)\n(* 3 4)\n(* a b)");
} catch (JessException je) {
System.out.print("An error occurred at line " +
je.getLineNumber());
System.out.println(" which looks like " +
je.getProgramText ());
System.out.println("Message: " + je.getMessage());
}
}
}
4.1 嵌套异常
Jess函数调用可能会调用Java方法,若Java方法抛出异常,
call
会创建一个
JessException
对象作为真实异常的包装并抛出。捕获
JessException
时,可能需要使用
getCause
方法检查是否有嵌套异常对象。示例代码如下:
import jess.*;
public class CheckNextException {
public static void main(String[] argv) {
try {
Rete r = new Rete();
r.executeCommand("(new java.net.URL foo://bar)");
} catch (JessException je) {
System.out.println(je.getMessage());
System.out.println(je.getCause().getMessage());
}
}
}
4.2 自定义异常
如果编写自己的Jess命令,可能需要创建自定义的
JessException
对象来报告错误。
JessException
有三个构造函数,主要区别在于最后一个参数。前两个参数始终是例程名称和错误消息。示例代码如下:
try {
Socket s = new Socket("www.google.com", 80);
// use the socket ...
} catch (Exception ex) {
throw new JessException("google", "Network error", ex);
}
5. 输入和输出
5.1 I/O路由器相关方法
Rete
类有以下方法处理I/O路由器:
-
addInputRouter(String name, Reader r, boolean mode)
:定义或重新定义输入路由器
-
getInputRouter(String name)
:返回指定名称的输入路由器
-
removeInputRouter(String name)
:取消定义输入路由器
-
getInputMode(String name)
:确定指定输入路由器是否类似控制台
-
addOutputRouter(String name, Writer w)
:定义或重新定义输出路由器
-
getOutputRouter(String name)
:返回指定名称的输出路由器
-
removeOutputRouter(String name)
:取消定义输出路由器
-
getOutStream()
:返回标准输出路由器
-
getErrStream()
:返回标准错误路由器
5.2 内置路由器
Jess有几个内置路由器,初始都连接到标准输入和输出。例如,
t
、
WSTDOUT
或
WSTDERR
的文本默认发送到
System.out
,从
t
或
WSTDIN
读取的数据来自
System.in
。
WSTDOUT
用于发送
Jess>
提示和表达式求值结果,
WSTDERR
用于内部错误消息,
WSTDIN
仅为对称存在,Jess不使用它。
5.3 使用自定义路由器
在命令行客户端中,内置路由器的默认值没问题,但如果Jess嵌入到带有GUI的软件中,打印到标准输出可能不合适。可以使用自定义的
Reader
和
Writer
对象设置标准路由器。例如,使用
java.io.StringWriter
定义一个名为
out
的新路由器:
StringWriter sw = new StringWriter();
Engine.addOutputRouter("out", sw);
执行
(printout out 12345 crlf)
后,调用
sw.toString()
将返回字符串
"12345\n"
。
在
jess.Console
图形控制台应用程序中,使用了
jess.awt.TextAreaWriter
和
jess.awt.TextReader
适配器类来处理GUI中的输入和输出。
TextAreaWriter
将文本追加到
java.awt.TextArea
,
TextReader
从内部缓冲区读取数据。
综上所述,Jess设计为可嵌入多种软件的库,具有广泛的Java API。
jess.Rete
类起着核心作用,可定义和检索规则、模板,管理I/O路由器。可以从Java处理
Fact
对象、添加JavaBeans,使用
JessException
处理错误,还能自定义I/O路由器。
6. 技术总结与应用展望
6.1 核心技术回顾
Jess作为一个可嵌入多种软件的库,其Java API提供了丰富的功能来实现与Java代码的交互。以下是对前面介绍的核心技术点的总结:
-
Fact对象操作
:处理多插槽、有序事实和删除事实的操作,需要注意多插槽值的存储和按编号查找事实的性能问题。
-
JavaBeans集成
:通过
Rete
类的
defclass
和
definstance
方法将JavaBeans添加到Jess工作内存,实现Java与Jess数据的交互。
-
Jess函数调用
:使用
Rete
类的方法和
jess.Funcall
类可从Java调用Jess函数。
-
异常处理
:
JessException
用于报告错误,需要注意嵌套异常和自定义异常的处理。
-
输入输出管理
:通过
Rete
类的方法管理I/O路由器,可自定义输入输出方式。
6.2 技术应用流程
下面是一个将上述技术综合应用的mermaid流程图,展示了从创建Jess引擎到处理JavaBeans、Fact对象,再到处理输入输出的完整流程:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B(创建Rete引擎):::process
B --> C(注册JavaBeans类):::process
C --> D(添加JavaBeans实例到工作内存):::process
D --> E(创建Fact对象):::process
E --> F{是否为多插槽事实}:::decision
F -->|是| G(设置多插槽值):::process
F -->|否| H(设置普通插槽值):::process
G --> I(断言Fact到工作内存):::process
H --> I
I --> J(调用Jess函数):::process
J --> K{是否需要处理输入输出}:::decision
K -->|是| L(设置I/O路由器):::process
K -->|否| M(继续其他操作):::process
L --> N(执行输入输出操作):::process
N --> O(结束操作):::process
M --> O
O --> P([结束]):::startend
6.3 未来应用方向
Jess的Java API为开发者提供了强大的工具,可应用于多种场景:
-
智能系统开发
:结合规则引擎和Java的面向对象编程,开发专家系统、决策支持系统等。
-
GUI应用集成
:将Jess嵌入到带有图形用户界面的Java应用中,实现用户交互和规则推理的结合。
-
Web应用开发
:在Web应用中使用Jess进行业务规则处理,提高系统的灵活性和可维护性。
6.4 注意事项与最佳实践
在使用Jess的Java API时,还需要注意以下几点:
-
性能优化
:避免频繁按编号查找事实,尽量保留对
Fact
对象的引用。
-
异常处理
:不要忽略
JessException
,至少打印异常信息到日志文件。
-
资源管理
:在使用自定义路由器时,确保正确管理
Reader
和
Writer
对象的生命周期。
以下是一个综合示例,展示了如何将上述技术应用到一个简单的Java程序中:
import jess.*;
import java.awt.Button;
import java.io.StringWriter;
public class JessIntegrationExample {
public static void main(String[] argv) {
try {
// 创建Rete引擎
Rete engine = new Rete();
// 注册JavaBeans类
engine.defclass("button", "java.awt.Button", null);
// 添加JavaBeans实例到工作内存
Button b = new Button("OK");
engine.definstance("button", b, true);
// 创建Fact对象
Deftemplate d = new Deftemplate("student", "A student", engine);
d.addSlot("name", Funcall.NIL, "STRING");
d.addMultiSlot("courses", Funcall.NILLIST);
engine.addDeftemplate(d);
Fact f = new Fact("student", engine);
f.setSlotValue("name", new Value("Fred Smith", RU.STRING));
ValueVector courses = new ValueVector();
courses.add(new Value("COMP 101", RU.STRING));
courses.add(new Value("HISTORY 202", RU.STRING));
f.setSlotValue("courses", new Value(courses, RU.LIST));
engine.assertFact(f);
// 调用Jess函数
Context context = engine.getGlobalContext();
Funcall watchFunc = new Funcall("watch", engine);
watchFunc.arg("all");
watchFunc.execute(context);
// 设置自定义路由器
StringWriter sw = new StringWriter();
engine.addOutputRouter("out", sw);
engine.executeCommand("(printout out 12345 crlf)");
System.out.println(sw.toString());
} catch (JessException je) {
System.out.println(je.toString());
}
}
}
通过以上步骤,开发者可以充分利用Jess的Java API,实现复杂的规则推理和数据交互。在实际应用中,根据具体需求灵活运用这些技术,不断优化和扩展系统功能。
Java与Jess交互的完整指南
超级会员免费看
79

被折叠的 条评论
为什么被折叠?



