31、Java中与Jess交互的全面指南

Java与Jess交互的完整指南

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,实现复杂的规则推理和数据交互。在实际应用中,根据具体需求灵活运用这些技术,不断优化和扩展系统功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值