Distribution
Category UC-411
SAND98-8206 (revised)
Unlimited Release
First Printed November 1997
Jess, The Java Expert System Shell
http://herzberg.ca.sandia.gov/jess
Ernest J. Friedman-Hill
Distributed Computing Systems
Sandia National Laboratories
Livermore, CA
Version 4.5 (May 3rd, 2001)
ABSTRACT
This report describes Jess, an expert system shell written
entirely in Java. Jess supports the development of rule-based expert
systems which can be tightly coupled to code written in the powerful,
portable Java language. The syntax of the Jess language is discussed,
and a comprehensive list of supported functions is presented. A guide
to calling Java functions from Jess, and to extending Jess by writing
Java code, is also included.
Note: this version of Jess is no longer being actively
developed. It is provided for download because it was the last version
of Jess to be Java 1.0 compatible. Note that this manual was written a
number of years ago and does not accurately describe the current Jess
software.
1 Introduction
Jess is an expert system shell written entirely in Java. Jess was
originally a clone of the essential core of
CLIPS, but has begun to
acquire a Java-influenced flavor of its own. With Jess, you can
conveniently give your Java applets and applications the ability to
"reason." In describing Jess, I am going to describe much of CLIPS
itself, but the reader may want to have a copy of the CLIPS manuals at
hand. See the CLIPS
site for more information.
Jess 4.5 is compatible with all versions of Java starting with version 1.0.2.
It is compatible with version 1.1, although while compiling you will see
warnings about deprecated methods. Such is the price of compatibility!
Note: Jess 4.5 is the last version that will be compatible with
Java 1.0.2; future versions of Jess will not work with anything less
than Java 1.1. That means that the "deprecated" warnings will disappear.
Jess is a work in progress; more features are constantly being added.
The order will be determined in part by what folks seem to want most, what
I need Jess to do, and how much time I have to spend on it. See
Version History for a list of what's
new in this version of Jess and see What's New in This
Release for a quick overview.
There is a Jess email discussion list you can join. To get information
about the jess-users list, send a message to majordomo@sandia.gov containing
the text
help
info jess-users
end
as the body of the message.
This is the final release of Jess 4.5. Although this version has
undergone extensive testing, It's always possible that there are bugs.
Please report any that you find to me at
ejfried@ca.sandia.gov so I
can fix them for a later release.
Jess is copyrighted software. See the file LICENSE
for details.
1.1 Getting Started With Jess
1.1.1 Unpacking the Distribution
If you download Jess for UNIX, you can extract the files using tar and
gunzip
gunzip Jess45.tgz
tar xf Jess45.tar
If you downloaded Jess for Windows, you get a .zip file which should be
unzipped using a Win32-aware unzip program like WinZip. Don't use PKUNZIP
since it cannot handle long file names.
When Jess is unpacked, you should have a directory named Jess45/.
Inside this directory may be the following files:
| README.html |
This file |
| jess/ |
If you have a source distribution, this is a directory containing
the jess package. There are many source files in here that
implement Jess's inference engine. Others implement a number of Jess
GUIs and command-line interfaces. Main.java implements the
Jess command-line interface. Console.java is a very simple
GUI console for Jess; ConsoleApplet.java is an applet version
of the same. If you have a binary distribution, this directory contains
.class files instead of .java files. |
| jess/view |
This directory contains Java source or .class files implementing
the optional Jess view command. |
| jess/reflect |
This directory contains Java source or .class files implementing
the optional Jess commands that let you create and manipulate Java
objects from Jess. |
| examples/ |
A directory of tiny example Jess files. |
| jess/examples |
A directory of more complicated examples, containing example Java source
files. |
| examples/index.html |
A web page containing the Jess example applet. It may need to be edited. |
| Makefile |
A simple makefile for Jess. |
1.1.2 Compiling Jess
If you have a source distribution of Jess, you have a set of Java
source files. To use Jess, you'll need to compile them first.
If you have a make utility (any UNIX make; or nmake
or GNU make on Win32), you can just run make and the
enclosed makefile will build everything. You might have to edit it a bit
first. Otherwise the commands:
javac jess/*.java (UNIX)
or
javac jess\*.java (Win32)
would work just fine, given that you have a Java compiler like Sun's JDK,
and that Jess45/ is your current directory. If you have problems,
be sure that the directory in which the jess subdirectory appears is on
your CLASSPATH; this may mean including . (dot). Don't
try to compile from inside the Jess45/jess/ directory; it won't
work. You can use either a Java 1.0.2 or a Java 1.1 compiler to compile
Jess. The resulting code runs on either 1.0 or 1.1 VMs. Note that if you
use a 1.1 compiler, you will see some warning about deprecated methods.
It is safe to ignore these warnings. Jess also seems to work with Java 1.2.
There are a number of optional source files in the subdirectories Jess45/jess/view/,
Jess45/jess/reflect/ and Jess45/jess/examples/ that
aren't compiled if you follow the instructions above. These files define
the optional debugging command view, the reflection commands new,
call, set, get, set-member, and get-member,
and the Java object matching commands defclass and definstance.
They can be compiled only with Java 1.1 or later. If you have such
a compiler, then you can issue a set of commands like:
javac jess/*.java jess/view/*.java jess/reflect/*.java jess/examples/*/*.java (Unix)
or
javac jess\*.java
javac jess\view\*.java
javac jess\reflect\*.java
javac jess\examples\pumps\*.java
javac jess\examples\simple\*.java (Win32)
to compile everything (or use the Makefile, of course).
Again, don't set your current directory to, for example,
Jess45/jess/examples/pumps/ to compile the pumps example: it will not
work. The compiler will report all sorts of errors about classes not being
found and the jess package not being found. Compile everything from the
Jess45 directory. I can't stress this enough: this is by far the most
common problem people have in getting started with
Jess!
1.1.3 Jess Example Programs
There are several example programs for you to try, including fullmab.clp,
zebra.clp, and wordgame.clp. fullmab.clp is
a version of the classic Monkey and Bananas problem. To run it yourself
from the command line, just type:
java jess.Main examples/fullmab.clp (Unix)
or
java jess.Main examples\fullmab.clp (Win32)
and the problem should run, producing a few screens of output. Any file
of Jess code can be run this way. Many simple CLIPS programs will also
run unchanged in Jess. Note that giving Jess a file name on the
command line is like using the batch command in
CLIPS. Therefore, you need to make sure that the file ends with:
(reset)
(run)
or nothing will happen. The zebra.clp and wordgame.clp
programs are two classic CLIPS examples selected to show how Jess deals
with tough situations. These examples both generate huge numbers of partial
pattern matches, so they are slow and use up a lot of memory. They each
can take some time to run, depending on your computer. Other examples
include sticks.clp (an interactive game) and frame.clp
(a demo of building a graphical interface using Jess's Java integration
capabilities).
In the jess/examples/* subdirectories, you will find some more complex
examples, all of which contain both Java and Jess code. As such, these
are generally examples of how to tie Jess and Java together. The
Pumps examples is a full working program that demonstrates how
Jess rules can react to the properties of Java Beans.
1.1.4 Command-line Interface
Jess has an interactive command-line interface. Just type java jess.Main
to get a Jess> prompt. To execute a file of CLIPS code from the
command prompt, use the batch command:
Jess> (batch myfile.clp)
(lots of output)
You can use the Jess system command to invoke an editor from the
Jess command line to edit a file of Jess code before reading it in with
batch. system also helps to allow non-Java programmers
to integrate Jess with other applications. Given that you have an editor
named notepad on your system, try:
Jess> (system notepad README &)
TRUE
The & character makes the editor run in the background. Omitting
it will keep the system command from returning until the called program
exits.
The class jess.Console is a graphical verison of the Jess command-line
interface. Output appears in a scrolling window. Type java jess.Console
to try it.
1.1.5 Jess as an Applet
The class jess.ConsoleApplet is a generic Jess applet that uses
the same display as the jess.Console class. It can be used in
general question-and-answer situations simply by embedding the applet class
on a Web page. The applet accepts two applet parameters. The value of an
INPUT parameter will be interpreted as a Jess program to run when
starting up. Note that when this program halts, the Jess prompt will appear
in the applet window. The applet also accept a COMPACT parameter.
If present, ConsoleApplet will contain only a bare-bones version
of Jess (no optional functions will be loaded).
1.2 What's New in This Release
If you've used Jess before, this section will help you get started quickly
with this version. Jess 4.5 offers quite a few new features and
user-visible changes:
-
Since the beginning, the standard Jess console and Applet classes have
been combined into one class. In the 4.0 release, the curiously named
QuizDisplay
class continued this ill-advised economy, combining an Applet, an application,
-and- a ReteDisplay subclass in one! This has been remedied: These classes
have been broken out into jess.Main, jess.Console, jess.ConsoleApplet,
and jess.ConsoleDisplay. The old 'Monkey and Banana' applet with the flashing
color lights has been (alas) removed and is no longer supported.
-
The Userfunction interface has changed slightly. name()
returns a String. RU.getAtom() and RU.putAtom()
are gone; there is no longer any need for them. A few public methods
here and there that used to accept integers or return integers now
return Strings or accept Strings as arguments; for example, the
name() methods of all the Def(X) classes now return String.
-
The GlobalContext class has disappeared, and the way execution
contexts are managed internally has been simplified. This should be an
invisible change for 99.9% of users.
-
Adding input routers has been slightly complicated, as you can specify
how the read command should behave with a given router.
-
A manual section has been added that better explains writing main() for
a Java app thet embeds Jess.
-
You can now supply a function call as a slot default value in a deftemplate;
and you can supply defaults for multislots. Furthermore, the new
default-dynamic deftemplate slot attribute lets you specify that a
default function call value for a slot be evaluated each time a
deftemplate fact is asserted.
-
Important! The way that defglobals behave in the face of
the (reset) command has changed to match CLIPS' behavior. The
set-reset-globals
and get-reset-globals commands have
been added to let you modify this. See their documentation for a better
description of the changes.
-
The set-strategy command has been added, bringing
user-selectable conflict resolution strategies to Jess.
-
The try command has been added, bringing exception handling
to Jess!
-
The new agenda command lets you see what will happen
next.
-
The set-salience-evaluation command
lets you use dynamic rule salience (a fairly advanced feature.)
-
The unique conditional element in
new. Important! You can no longer use the atom 'unique'
as the head of a deftemplate as a result. Unique lets you
give hints to the Rete engine and can theoretically result in speedups
of up to 50%; in practice I've observered real speedups of 20-30%; for
examples, see the wordgame and zebra sample programs.
-
The store and fetch functions (both in Jess and as Java
member functions of the jess.Rete class) let you easily pass
values between Jess and Java, from simple types to Java objects.
-
The jess.reflect.Canvas class has been
added. It lets you write GUIs in Jess that include drawing and
painting, without writing any Java code.
2 The Jess Language
Jess is an interpreter for a rule language borrowed from CLIPS. Given CLIPS's
heritage (strongly influenced by systems written in LISP), this rule language
is basically a small, idiosyncratic version of LISP, making Jess a LISP
interpreter written in Java. I will briefly describe this language here;
more information can be gotten from the CLIPS manuals themselves.
I'm using an extremely informal notation to describe syntax. Basically
strings in <angle-brackets> are some kind of data that must be supplied;
things in [square brackets] are optional, things ending with +
can appear one or more times, and things ending with * can appear
zero or more times.
In general, input to Jess is free-format. Newlines are generally not
significant and are treated as whitespace.
In the example dialogs, you type what appears after the Jess>
prompt. The system responds with the text in bold.
2.1 Atoms
The atom or symbol is a core concept of the Jess language. Atoms are very
much like identifiers in other languages. A Jess atom can contain letters,
numbers, and the following punctuation: $*=+/<>_?#. . An atom
may not begin with a number; it may begin with some punctuation marks (some
have special meanings as operators when they appear at the start of an
atom). The best atoms consist of letters, numbers, underscores, and dashes;
dashes are traditional word separators. The following are all valid atoms:
foo first-value contestant#1 _abc
2.2 Numbers
Jess parses numbers using the Java StreamTokenizer class. Therefore,
it accepts only simple floating point and integer numbers. It does not
accept scientific or engineering notation. The following are all valid
numbers:
3 4. 5.643
2.3 Strings
Character strings in Jess are denoted using double quotes
("").
Backslashes (\) can be used to escape embedded quote symbols.
The following are all valid strings:
"foo" "Hello, World" "\"Nonsense,\" he said firmly."
2.4 Lists
The fundamental unit of syntax in Jess is the list. A list always consists
of an enclosing set of parentheses and zero or more atoms, numbers, strings,
or other lists. The following are valid lists:
(+ 3 2) (a b c) ("Hello, World") () (deftemplate foo (slot bar))
The first element of a list (the car of the list in LISP parlance)
is often called the list's head in Jess.
2.5 Comments
Programmer's comments in Jess begin with a semicolon (;) and extend
to the end of the line of text. Here is an example of a comment:
; This is a list
(a b c)
2.6 Functions
Jess contains a large number of built-in functions that you may call. More
functions are provided as extensions. You can write your own functions
in the Jess language (see Deffunctions) or
in Java (see Extending Jess with Java).
Function calls in Jess use a prefix notation. A list whose head is an
atom that is the name of an existing function can be evaluated as an expression.
For example, an expression that uses the + function to add the
numbers 2 and 3 would be written (+ 2 3). When
evaluated, the value of this expression is the number 5 (not a
list containing the single element 5!). In general, expressions
are recognized as such and evaluated in context when appropriate. You can
type expressions at the Jess> prompt. Jess evaluates the expression
and prints the result:
Jess> (+ 2 3)
5
Jess> (+ (+ 2 3) (* 3 3))
14
Note that arithmetic results may be returned as floating-point numbers
or as integers, depending on the types of the arguments.
Jess implements only a small subset of CLIPS functions as intrinsic
functions that are built into Jess and cannot be removed. All of these
have been designed to function as much like their CLIPS counterparts as
possible. On the other hand, I'm supplying implementations for many
more CLIPS functions, and lots of functionality specific to Jess,
as 'Userfunctions' - external functions written in Java that you can plug
into Jess. All of the included Userfunctions are installed into the command-line version
of Jess by default; you can pick and choose in your own applications. In
applets, in particular, you may want to include only the Userfunctions
you need, to keep the size of the applet down. (see Extending
Jess with Java for information about doing this.)
Here is the complete list of functions shipped with Jess 4.5, both
intrinsic and optional:
* ** + - / < <= <> = > >= abs agenda and assert assert-string bag
batch bind build call clear close complement$ create$ defclass
definstance delete$ div e engine eq eq* eval evenp exit exp explode$
external-addressp facts fetch first$ float floatp foreach format
gensym* get get-member get-reset-globals get-salience-evaluation
get-var halt if implode$ insert$ integer integerp intersection$
jess-version-number jess-version-string length$ lexemep list-function$
load-facts load-function load-package log log10 lowcase max member$
min mod modify multifieldp neq new not nth$ numberp oddp open or pi
ppdefrule printout random read readline replace$ reset rest$ retract
retract-string return round rules run save-facts set set-member
set-reset-globals set-salience-evaluation set-strategy setgen socket
sqrt store str-cat str-compare str-index str-length stringp sub-string
subseq$ subsetp sym-cat symbolp system time try undefinstance
undefrule union$ unwatch upcase view watch while
All these functions are described in detail in the
Jess Function Guide. Note that the
distinction between intrinsic functions and Userfunctions is mostly an
academic one; intrinsic functions are all written as Java classes that
implement the same Userfunction interface that user-supplied
classes do. The only real difference is whether Jess will start up
without them; the intrinsics are required because they're loaded in by
code in the jess.Funcall class. To find out if a function is
intrinsic, see its entry in the Function Guide
below.
2.7 Variables
Programming variables in Jess are atoms that begin with the question mark
(?) character. The question mark is part of the variable's name.
A normal variable can refer to a single atom, number, or string. A variable
whose first character is instead a $ (for example, $?X)
is a multivariable, which can refer to a special kind of list called
multifield. You assign to any variable using the bind function:
(bind ?x "The value")
Multifields are generally created using special multifield functions like
create$ and can then be bound to multivariables:
(bind $?grocery-list (create$ eggs bread milk))
Variables need not (and cannot) be declared before their first use (except
for Defglobals).
2.8 Constructs
Besides expressions and multifields, the Jess language includes another
kind of special list called a construct. A construct is a list that
defines something to the Jess system itself. For example, the deffunction
construct is used to define functions (see Deffunctions).
A construct evaluates to TRUE if it was accepted by Jess or
FALSE if it was not.
2.9 Deffunctions
The deffunction construct is used to define functions that you
can then call from Jess. A deffunction construct looks like this:
(deffunction <function-name> [<doc-comment>] (<parameter>*)
<expr>*
[<return-specifier>])
The <function-name> must be an atom. Each <parameter>
must be a variable name (all functions use pass-by-value semantics). The
optional <doc-comment> is a double-quoted string that can describe
the purpose of the function. There may be an arbitrary number of <expr>
expressions. The optional <return-specifier> gives the return
value of the function. It can either be an explicit use of the return
function or it can be any value or expression. Control flow in deffunctions
is achieved via the special control-flow expressions foreach,
if, and while. The following is a deffunction
that returns the numerically larger of its two numeric arguments:
(deffunction max (?a ?b)
(if (> ?a ?b) then
(return ?a)
else
(return ?b)))
Note that this could have also been written as:
(deffunction max (?a ?b)
(if (> ?a ?b) then
?a
else
?b))
2.10 Facts
Jess maintains a list of facts or information about the current state of
the system. Facts may be ordered or unordered. Ordered facts
are merely lists whose head must be an atom:
(temperature 98.6)
(shopping-list bread milk paper-towels)
(start-processing)
Unordered facts are structured. They contain a definite set of slots
which must be accessed by name. While ordered facts can be used without
prior definition, unordered facts must be defined using the deftemplate
construct (see Deftemplates).
Facts are placed on the fact list by the assert function. You
can see the current fact list using the facts function. You can
remove (retract) a fact from the fact list if you know its fact
ID. For example:
Jess> (assert (foo bar))
<Fact-0>
Jess> (facts)
f-0 (foo bar)
For a total of 1 facts.
TRUE
Jess> (retract 0)
TRUE
Jess> (facts)
For a total of 0 facts.
TRUE
2.11 Deftemplates
To define an unordered fact, use the deftemplate construct:
(deftemplate <deftemplate-name> [<doc-comment>]
[(slot <slot-name> [(default <value>)]
[(default-dynamic <value>)]
[(type <typespec>)])]+)
The <deftemplate-name> is the head of the facts that will be
created using this deftemplate. There may be an arbitrary number
of slots. The <slot-name> must be an atom. The default
slot qualifier states that the default value of a slot in a new fact is
given by <value>; the default is the atom nil.
The 'default-dynamic' version will evaluate the given function each
time a new fact using this template is asserted. The 'type' slot
qualifier is accepted (for compatibility with CLIPS) but is ignored by Jess.
As an example, defining the following deftemplate:
(deftemplate automobile
"A specific car."
(slot make)
(slot model)
(slot year)
(slot color (default white)))
would allow you to define facts like this:
Jess> (assert (automobile (make Chrysler) (model LeBaron) (year 1997)))
<Fact-0>
Jess> (facts)
f-0 (automobile (make Chrysler) (model LeBaron) (year 1997) (color white))
For a total of 1 facts.
TRUE
Note that the car is white by default. Also note that any number of additional
automobiles could also be simultaneously asserted onto the fact list using
this deftemplate.
A given slot in a deftemplate fact can normally hold only one
value. If you want a slot that can hold multiple values, use the multislot
keyword instead:
(deftemplate box
(slot location)
(multislot contents))
(assert (box (location kitchen) (contents spatula sponge frying-pan)))
2.12 Defclasses
A defclass construct basically lets you use a Java Bean as a
deftemplate. Almost any Java object can be made into a
Bean. This is a very powerful feature of Jess that lets it reason
about the state of objects connected to the physical world.
defclass will be documented later,
after we've explained some of the prerequisites.
2.13 Deffacts
The deffacts construct is a handy way to define a list of facts that should
be made true when the Jess system is started or reset.
(deffacts <deffacts-name>
[<doc-comment>]
<fact>+)
The primary purpose of the <deffacts-name> is documentation.
A deffacts instance can contain any number of facts. Any unordered
facts in a deffacts instance must have previously been defined
via a deftemplate construct when the deffacts is parsed.
The following is a valid deffacts construct:
(deffacts automobiles
(automobile (make Chrysler) (model LeBaron) (year 1997))
(automobile (make Ford) (model Contour) (year 1996))
(automobile (make Nash) (model Rambler) (year 1948)))
2.14 Definstances
What deffacts are to deftemplates,
definstances are to defclasses.
While a deffacts construct defines an initial set of facts to
the Rete engine, the definstance construct tells Jess that one
particular Java object should be treated as if it were a fact and be matched
by deftemplate patterns defined in defclass
constructs. Again, we'll defer discussion until we're in a better
position to understand the mechanics involved.
2.15 Defrules
The main purpose of an expert shell like Jess is to support the execution
of rules. Rules in Jess are somewhat like the IF...THEN... statements of
other programming languages. In operation, Jess constantly tests to see
if any of the IFs become true, and executes the corresponding THENs. (Actually,
it doesn't work quite this way, but this is a good way to imagine things.
See How Jess Works for an explanation closer
to the truth.) The intelligence embedded in an intelligent rule-based system
is encoded in the rules. The defrule construct is used to define
a rule to Jess:
(defrule <defrule-name>
[<doc-comment>]
[<salience-declaration>]
[[<pattern-binding> <- ] <pattern>]*
=>
<action>*)
Basically, a rule consists of a list of patterns (the IF part on the rule's
left-hand-side or LHS) and a list of actions (the THEN part on the rule's
right-hand-side or RHS). The patterns are matched against the fact list.
When facts are found that match all the patterns of a rule, the rule becomes
activated, meaning it may be fired (have its actions executed).
Note: The patterns on rule LHSs are matched against the
fact-list as if they were facts - they are NOT function calls!
The following rule does NOT work:
(defrule wrong-rule
(eq (+ 2 2) 4)
=>
(printout t "Just as I thought, 2 + 2 = 4!" crlf))
This rule will NOT fire just because the function call (eq (+ 2 2) 4)
would evaluate to true. Instead, Jess will try to find a fact on the
fact-list that looks like (eq 4 4). Unless you have previously
asserted such a fact, this rule will NOT be activated and will
not fire. If you want to fire a rule based on the evaluation of a
function, you can use the test CE.
An activated
rule may become deactivated before firing if the facts that matched its
patterns are retracted, or removed from the fact list, while it is waiting
to be fired. Here is an example of a simple rule:
(defrule example-1
"Announce 'a b c' facts"
(a b c)
=>
(printout t "Saw 'a b c'!" crlf))
To see this rule in action, enter it at the Jess> prompt, assert
the fact (a b c), then the run command to start the Jess
engine. You'll get some interesting additional information by first issuing
the watch all command:
Jess> (clear)
TRUE
Jess> (watch all)
TRUE
Jess> (defrule example-1
"Announce 'a b c' facts"
(a b c)
=>
(printout t "Saw 'a b c'!" crlf))
example-1: +1+1+1+1+t
TRUE
Jess> (assert (a b c))
==> Activation: example-1 : f-0
==> (a b c)
<Fact-0>
Jess> (run)
FIRE example-1 f-0
Saw 'a b c'!
TRUE
Jess>
When you enter the rule, you see the sequence of symbols +1+1+1+1+t.
This tells you something about the way that Jess compiled the rule you
wrote into the internal rule representation. Then when you assert the fact,
Jess responds by telling you that the new fact was assigned the numeric
fact identifier 0 (f-0), and that it is an ordered fact with head
a and additional fields b and c. Then it tells
you that the rule example-1 is activated by the fact f-0, that fact you
just entered. When you type the run command, you see an indication
that your rule has been fired, including a list of the relevant fact IDs.
The line "Saw 'a b c'!" is the result the execution of your rule.
Multiple activated rules are fired in order of salience (see Salience).
Within a given salience value, the order in which rules will fire is given
by the current conflict resolution strategy. See the
set-strategy command for
details. You can see the list of activated, but not yet fired,
rules with the command.
If all the patterns of a rule had to be given literally as above, Jess
would not be very powerful. However, patterns can also include wildcards
and various kinds of predicates (comparisons and boolean functions).
You can specify a variable name instead of a value for a field in any of
a rule's patterns (but not the pattern's head). A variable matches any
value in that position within a rule. For example, the rule:
(defrule example-2
(a ?x ?y)
=>
(printout t "Saw 'a " ?x " " ?y "'" crlf))
will be activated each time any fact with head a having two fields
is asserted: (a b c), (a 1 2), (a a a), and
so forth. As in the example, the variables thus matched in the patterns
(or LHS) of a rule are available in the actions (RHS) of the same rule.
Each such variable field in a pattern can also include any number of
tests to qualify what it will match. Tests follow the variable name and
are separated from it and from each other by ampersands. (The variable
name itself is actually optional.) Tests can be:
-
A literal value (in which case the variable matches only that value).
-
Another variable (which must have been matched earlier in the rule's LHS).
This will constrain the field to contain the same value as the variable
was first bound to.
-
A colon (:) followed by a function call, in which case the test
succeeds if the function returns the special value TRUE. These
are called predicate constraints.
-
An equals sign (=) followed by a function call. In this case the
field must match the return value of the function call. These are called
return value constraints. Note that both predicate constraints and
return-value constraints can refer to variables bound elsewhere in this
or any preceding pattern in the same defrule. Note: pretty-printing
a rule containing a return value contstraint will show that it has been
transformed into an equivalent predicate constraint.
-
Any of the other options preceded by a tilde (~), in which case
the sense of the test is reversed (inequality or false).
Here's an example of a rule that uses several kinds of tests:
(defrule example-3
(not-b-and-c ?n1&~b ?n2&~c)
(different ?d1 ?d2&~?d1)
(same ?s ?s)
(more-than-one-hundred ?m&:(> ?m 100))
=>
(printout t "Found what I wanted!" crlf))
The first pattern will match a fact with head not-b-and-c with
exactly two fields such that the first is not b and the second
is not c. The second pattern will match any fact with head different
and two fields such that the two fields have different values. The third
pattern will match a fact with head same and two fields with identical
values. The last pattern matches a fact with head more-than-one-hundred
and a single field with a numeric value greater than 100.
A few more details about patterns: you can match a field without binding
it to a variable by omitting the variable name and using just a question
mark (?) as a placeholder. You can match any number of fields
using a multivariable (one starting with $?):
Jess> (defrule example-4
(grocery-list $?list)
=>
(printout t "I need to buy " $?list crlf))
TRUE
Jess> (assert (grocery-list eggs milk bacon))
TRUE
Jess> (run)
I need to buy (eggs milk bacon)
TRUE
And finally, to access a global variable on the left-hand side of a
rule, you must use the get-var function.
2.15.1 Pattern bindings.
Sometimes you need a handle to an actual fact that helped to activate a
rule. For example, when the rule fires, you may need to retract or modify
the fact. To do this, you use a pattern-binding variable:
(defrule example-5
?fact <- (command "retract me")
=>
(retract ?fact))
The variable (?fact, in this case) is assigned the fact ID of
the particular fact that activated the rule.
2.15.2 Salience.
Rules normally fire in an order related to which rules were most recently
activated. See the set-strategy command
for details. To force certain rules to always fire first or last, rules
can include a salience declaration:
(defrule example-6
(declare (salience -100))
(command exit-when-idle)
=>
(printout t "exiting..." crlf))
Declaring a low salience value for a rule makes it fire after all other
rules of higher salience. A high value makes a rule fire before all rules
of lower salience. The default salience value is zero. Salience values
can be integers, global variables, or function calls. See the set-salience-evaluation command
for details about when such function calls will be evaluated.
2.15.3 Not patterns.
A pattern can be enclosed in a list with not as the head. In this
case, the pattern is considered to match if a fact which matches the pattern
is not found. For example:
(defrule example-7
(person ?x)
(not (married ?x))
=>
(printout t ?x " is not married!" crlf))
Note that a not pattern cannot contain any variables that are
not bound before that pattern (since a not pattern does not match
any facts, it cannot be used to define the values of any variables!) You
can use blank variables, however (a blank variable is a bare ?
or $?). A not pattern can similarly not have a pattern
binding.
2.15.4 The test conditional element (CE).
A pattern with test as the head is special; the body consists
not of slot tests but of a single function which is evaluated and whose
truth determines whether the pattern matches. For example:
(defrule example-8
(person (age ?x))
(test (> ?x 30))
=>
(printout t ?x " is over 30!" crlf))
Note that a test pattern, like a not, cannot contain
any variables that are not bound before that pattern. test and
not may be combined:
(not (test (eq ?X 3)))
is equivalent to:
(test (neq ?X 3))
2.15.5 The unique conditional element.
A pattern can be enclosed in a list with unique as the head.
This is a hint to Jess that only one fact could possibly satisfy a given
pattern, given matches for the preceding patterns in that rule. Here's
an example:
(defrule unique-demo
(tax-form (social-security-number ?num))
(unique (person (social-security-number ?num) (name ?name)))
=>
(printout t "Auditing " ?name "..." crlf))
Here the unique CE is providing a hint to Jess that only one person
can have a given Social Security number. Given this knowledge, Jess knows
that once it has found the person that matches a given tax form, it doesn't
need to look any further. In practice, this can result in performance gains
of 20-30% on real problems!
unique may not be combined in the same patten with either
test or not CEs.
unique was new in Jess 4.1, and is my own invention. I'm interested
in hearing any feedback related to this feature.
2.16 Defglobals
Jess can support global variables that are visible from the command-prompt
or inside any rule or deffunction. You can define them using the defglobal
construct:
(defglobal
[<varname1> = <value1>]*)
Note that defglobals are reset to their assigned values by the (reset)
command. If the <value> is a function call, this function will be evaluated
each time (reset) is called. You can change this behaviour with the set-reset-globals
command.
2.17 Things Not Implemented In Jess
Jess does not implement all features of all CLIPS constructs. This list
tries to explain some of what's missing from Jess to those who know CLIPS.
If you're not already a CLIPS user, you can skip this section.
2.17.1 Defrules
-
The and and or conditional elements (CEs) are not supported
on rule LHSs. not is supported, however. You can generally use
multiple rules to simulate the effect of an and or or
CE.
-
The | connective constraint is not supported. Note that instead
of writing a pattern like:
(foo bar|baz)
you can write:
(foo ?x&:(or (eq ?X bar) (eq ?X baz)))
to achieve the same effect in Jess.
2.17.2 Deffunctions.
Forward declarations of mutually recursive functions are not needed in
Jess and will not parse.
2.17.3 Deftemplates.
The only supported slot attribute in Jess are the default and
default-dynamic attributes.
In particular, type will parse, but is ignored at runtime.
2.17.4 COOL, FuzzyCLIPS, wxCLIPS, etc.
Jess does not implement any features of these CLIPS extensions. Note that
defclass and definstance are keywords in CLIPS that form
part of COOL. Although these keywords exist in Jess, their syntax and precise
meaning is different. You should find that the functionality they
provide (pattern matching on Java Beans) is a satisfactory replacement
for COOL.
2.17.5 Modules.
Jess does not implement CLIPS modules. However, since Jess itself is object-oriented,
you can instantiate multiple Jess systems and get them to communicate via
the external function interface.
3 Jess Function Guide
In this section, every Jess language function shipped with Jess version
4.5 is described. Some of these functions are intrinsic functions while
others are Userfunctions and may not be available to all Jess code. All
of these functions are installed into the command-line version of Jess;
to use a function not marked (built-in) in your own programs, you need
to add the appropriate Userpackage using Rete.addUserpackage(new
<pkgname>()). The package for each function is listed below.
Note: many functions documented as requiring a specific minimum
number of arguments will actually return sensible results with fewer;
for example, the + function will return the value of a single
argument as its result. This behavior is to be regarded as
undocumented and unsupported. In addition, all functions documented as
requiring a specific number of arguments will not report an error if
invoked with more than that number; extra rguments are simply ignored.
(* <numeric-expression> <numeric-expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
Two or more numeric expressions
-
Returns:
-
Number
-
Description:
-
Returns the products of its arguments. The return value is an INTEGER
unless any of the arguments are FLOAT, in which case it is a FLOAT.
(** <numeric-expression> <numeric-expression>)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
Two numeric expressions
-
Returns:
-
Number
-
Description:
-
Raises its first argument to the power of its second argument (using Java's
Math.pow() function). Note: the return value is
NaN (not a number) if both arguments are negative.
(+ <numeric-expression> <numeric-expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
Two or more numeric expressions
-
Returns:
-
Number
-
Description:
-
Returns the sum of its arguments. The return value is an INTEGER
unless any of the arguments are FLOAT, in which case it is a FLOAT.
Note: the return value is the value of the single numeric
expression if only one argument is supplied.
(- <numeric-expression> <numeric-expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
Two or more numeric expressions
-
Returns:
-
Number
-
Description:
-
Returns the first argument minus all subsequent arguments. The return value
is an INTEGER unless any of the arguments are FLOAT,
in which case it is a FLOAT.
(/ <numeric-expression> <numeric-expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
Two or more numeric expressions
-
Returns:
-
Number
-
Description:
-
Returns the first argument divided by all subsequent arguments. The return
value is a FLOAT.
(< <numeric-expression> <numeric-expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
Two or more numeric expressions
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE if each argument is less in value than the argument
following it; otherwise, returns FALSE.
(<= <numeric-expression> <numeric-expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
Two or more numeric expressions
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE if the value of each argument is less than or equal
to the value of the argument following it; otherwise, returns FALSE.
(<> <numeric-expression> <numeric-expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
Two or more numeric expressions
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE if the value of the first argument is not equal in
value to all subsequent arguments; otherwise returns FALSE.
(= <numeric-expression> <numeric-expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
Two or more numeric expressions
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE if the value of the first argument is equal in value
to all subsequent arguments; otherwise, returns FALSE. The integer
2 and the float 2.0 are =, but not eq.
(> <numeric-expression> <numeric-expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
Two or more numeric expressions
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE if the value of each argument is less than that of
the argument following it; otherwise, returns FALSE.
(>= <numeric-expression> <numeric-expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
Two or more numeric expressions
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE if the value of each argument is greater than or
equal to that of the argument following it; otherwise, returns FALSE.
(abs <numeric-expression>)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
One numeric expression
-
Returns:
-
Number
-
Description:
-
Returns the absolute value of its only argument.
(agenda)
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
None
-
Returns:
-
NIL
-
Description:
-
Displays a list of rule activations to the WSTDOUT router.
(and <expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
One or more expressions
-
Returns:
-
Boolean
-
Description:
Returns TRUE if all arguments evaluate to a non-FALSE
value; otherwise, returns FALSE.
(assert <RHS-pattern>+)
-
Package:
-
(built-in)
-
Arguments:
-
One or more facts (not fact-IDs)
-
Returns:
-
Fact-ID or FALSE
-
Description:
-
Adds a fact to the fact list. Asserts all facts onto the fact list; returns
the fact-ID of last fact asserted or FALSE if no facts were successfully
asserted (for example, if all facts given are duplicates of existing facts.)
(assert-string <string-expression>)
-
Package:
-
(built-in)
-
Arguments:
-
One string representing a fact
-
Returns:
-
Fact-ID or FALSE
-
Description:
-
Converts a string into a fact and asserts it. Attempts to parse string
as a fact, and if successful, returns the value returned by assert with
the same fact. Note that the string must contain the fact's enclosing parentheses.
(bag <bag-command> <bag-arguments>+)
-
Package:
-
jess.BagFunctions
-
Arguments:
-
An atom (a sub-command) and one or more additional arguments
-
Returns:
-
(Varies)
-
Description:
-
The bag command lets you manipulate Java hashtables from Jess.
The net result is that you can create any number of associative arrays
or property lists. Each such array or list has a name by which it can be
looked up. The lists can contain other lists as properties, or any other
Jess data type.
The bag command does different things based on its first argument.
It's really seven commands in one:
-
create accepts a String, the name of a new Bag to be created.
The bag object itself is returned. For example:
Jess> (bag create my-bag) <External-Address> Jess>
delete accepts the name of an existing bag, and deletes it from
the list of bags.
find accepts the name of a bag, and returns the corresponding
bag object, if one exists, or nil.
list returns a list of the names of all the existing bags, as
a multifield.
set accepts as arguments a bag, a String property name, and any
Jess value as its three arguments. The named property of the given bag
is set to the value, and the value is returned.
get accepts as arguments a bag and a String property name. The
named property is retrieved and returned, or nil if there is no
such property. For example:
Jess> (defglobal ?*bag* = 0)
TRUE
Jess> (bind ?*bag* (bag create my-bag))
<External-Address>
Jess> (bag set ?*bag* my-prop 3.0)
3.0
Jess> (bag get ?*bag* my-prop)
3.0
props accepts a bag as the single argument and returns a multifield
consisting of a list of the names of all the properties of that bag.
(batch)
-
Package:
-
jess.Miscfunctions
-
Arguments:
-
One string or atom representing the name of a file
-
Returns:
-
(Varies)
-
Description:
-
Attempts to parse and evaluate the given file as Jess code. If successful,
returns the return value of the last expression in the file.
Note: the argument must follow Jess' rules for valid
atoms or strings. On UNIX systems, this presents no particular
problems, but Win32 filenames may need special treatment. In
particular: pathnames should use either '\\' (double backslash) or '/'
(forward slash) instead of '\' (single backslash) as directory
separators; and pathnames which include a colon (':') or a
space character (' ') must be enclosed in double quotes.
(bind <variable> <expression>*)
-
Package:
-
(built-in)
-
Arguments:
-
A variable name and any value
-
Returns:
-
(Varies)
-
Description:
-
Binds a variable to a new value. Assigns the given value to the given variable,
creating the variable if necessary. Note that (as in CLIPS) this works
best in rules and deffunctions, and not from the command prompt. Returns
the given value.
(build <lexeme-expression>)
-
Package:
-
jess.Miscfunctions
-
Arguments:
-
One string representing some Jess code
-
Returns:
-
(Varies)
-
Description:
-
Evaluates a string as though it were entered at the command prompt. Only
allows constructs to be evaluated. Attempts to parse and evaluate the given
string as Jess code. If successful, returns the return value of the last
expression in the string. This is typically used to define rules from Jess
code. For instance:
(build "(defrule foo (foo) => (bar))")
(call (<external-address> | <string-expression>) <string-expression>
<call-arguments>+)
-
Package:
-
jess.reflect.ReflectFunctions
-
Arguments:
-
an external address or String, a String, and any number of additional arguments
(see below)
-
Returns:
-
(Varies)
-
Description:
-
Calls a Java method on the given object, or a static method of the class
named by the first argument. The second argument is the name of the method,
and subsequent arguments are passed to the method. Arguments are promoted
and overloaded methods selected precisely as for new.
The return value is converted to a suitable Jess value before being returned.
Array return values are converted to multifields.
The functor call may be omitted if the method being called
is non-static and the object is represented by a simple variable. The following
two method calls are equivalent:
;; These are legal and equivalent
(call ?vector addElement (new java.lang.String "Foo"))
(?vector addElement (new java.lang.String "Foo"))
call may not be omitted if the object comes from the return value
of another function call:
;; This is illegal
((new java.lang.Vector 10) addElement (new java.lang.String "Foo"))
(clear)
-
Package:
-
(built-in)
-
Arguments:
-
None
-
Returns:
-
TRUE
-
Description:
-
Clears Jess. Deletes all rules, deffacts, defglobals, deftemplates, facts,
activations, and so forth. Userfunctions are not deleted.
(close [<router-identifier>])
-
Package:
-
(built-in)
-
Arguments:
-
One or more router identifiers (atoms)
-
Returns:
-
TRUE
-
Description:
-
Closes any I/O routers associated with the given name by calling close()
on the underlying stream, then removes the routers. Any subsequent attempt
to use a closed router will report bad router. See open.
(complement$ <multifield-expression> <multifield-expression>)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
Two multifields
-
Returns:
-
Multifield
-
Description:
-
Returns a new multifield consisting of all elements of the second multifield
not appearing in the first multifield.
(create$ <expression>*)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
Zero or more expressions
-
Returns:
-
Multifield
-
Description:
-
Appends its arguments together to create a multifield value. Returns a
new multifield containing all the given arguments. Note:
multifields must be created explicitly using this function or others that
return them. Multifields cannot be directly parsed from Jess input.
(delete$ <multifield-expression> <begin-integer-expression> <end-integer-expression>)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
A multifield and two integer expressions
-
Returns:
-
Multifield
-
Description:
-
Deletes the specified range from a multifield value. The first numeric
expression is the 1-based index of the first element to remove; the second
is the 1-based index of the last element to remove.
(div <numeric-expression> <numeric-expression>+)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
Two or more numeric expressions
-
Returns:
-
Numbers
-
Description:
-
Returns the first argument divided by all subsequent arguments using integer
division. Quotient of the values of the two numeric expressions rounded
to the nearest integer.
(e)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
None
-
Returns:
-
Number
-
Description:
-
Returns the transcendental number e.
(engine)
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
None
-
Returns:
-
External address
-
Description:
-
Returns an external-address object containing the Rete engine in which
the function in called.
(eq <expression> <expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
Two or more arbitrary arguments
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE if the first argument is equal in type and value
to all subsequent arguments. For strings, this means identical contents.
Uses the Java Object.equals() function, so can be redefined for
external types. Note that the integer 2 and the floating-point
number 2.0 are not eq, but they are eq*
and =.
(eq* <expression> <expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
Two or more arbitrary arguments
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE if the first argument is equivalent to all the others.
Uses numeric equality for numeric types, unlike eq. Note that
the integer 2 and the floating-point number 2.0 are not eq,
but they are eq* and =.
(eval <lexeme-expression>)
-
Package:
-
(built-in)
-
Arguments:
-
One string containing a valid Jess expression
-
Returns:
-
(Varies)
-
Description:
-
Evaluates a string as though it were entered at a command prompt. Only
allows functions to be evaluated. Evaluates the string as if entered at
the command line and returns the result.
(evenp <expression>)
-
Package:
-
jess.PredFunctions
-
Arguments:
-
One numeric expression
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE for even numbers; otherwise, returns FALSE.
Results with non-integers may be unpredictable.
(exit)
-
Package:
-
(built-in)
-
Arguments:
-
None
-
Returns:
-
Nothing
-
Description:
-
Exits Jess and halts Java.
(exp <numeric-expression>)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
One numeric expression
-
Returns:
-
Number
-
Description:
-
Raises the value e to the power of its only argument.
(explode$ <string-expression>)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
One string
-
Returns:
-
Multifield
-
Description:
-
Creates a multifield value from a string. Parses the string as if by a
succession of read calls, then returns these individual values
as the elements of a multifield.
(external-addressp <expression>)
-
Package:
-
jess.PredFunctions
-
Arguments:
-
One expression
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE or FALSE as the given expression is an external-address.
(facts)
-
Package:
-
(built-in)
-
Arguments:
-
None
-
Returns:
-
TRUE
-
Description:
-
Prints a list of all facts on the fact list.
(fetch <string or atom>)
-
Package:
-
(built-in)
-
Arguments:
-
One string or atom
-
Returns:
-
(varies)
-
Description:
-
Retrieves and returns any value previously stored by the
store function under the given
name, or nil if there is none. Analogous to the
fetch() member function of the Rete class. See
the section "Using store and
fetch" for details.
(first$ <multifield-expression>)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
One multifield
-
Returns:
-
Multifield
-
Description:
-
Returns the first field of a multifield.as a new 1-element multifield.
(float <numeric-expression>)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
One numeric expression
-
Returns:
-
Floating-point number
-
Description:
-
Converts its only argument to a float.
(floatp <expression>)
-
Package:
-
jess.PredFunctions
-
Arguments:
-
One numeric expression
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE for floats; otherwise, returns FALSE.
(foreach <variable> <multifield-expression> <action>*)
-
Package:
-
(built-in)
-
Arguments:
-
A variable, a multifield expression, and zero or more arguments
-
Returns:
-
Varies
-
Description:
-
The named variable is set to each of the values in the multifield in turn;
for each value, all of the other arguments are evaluated in order. The
return function can be used to break the iteration.
Example:
(foreach ?x (create$ a b c d) (printout t ?x crlf))
(format <router-identifier> <string-expression> <expression>*)
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
A router identifier, a format string, and zero or more arguments
-
Returns:
-
A string
-
Description:
-
Sends formatted output to the specified logical name. Formats the arguments
into a string according to the format string, which is identical to that
used by printf in the C language (find a C book for more information).
Returns the string, and optionally prints the string to the named router.
If you pass nil for the router name, no printing is done.
(get <external-address> <string-expression>)
-
Package:
-
jess.reflect.ReflectFunctions
-
Arguments:
-
An external address and a string.
-
Returns:
-
(Varies)
-
Description:
-
Retrieves the value of a Java Bean's property. The first argument is the
object and the second argument is the name of the property. The return
value is converted to a suitable Jess value exactly as for call.
(get-member (<external-address> | <string-expression>)
<string-expression>)
-
Package:
-
jess.reflect,ReflectFunctions
-
Arguments:
-
An external address or a string, and a string.
-
Returns:
-
(Varies)
-
Description:
-
Retrieves the value of a Java object's data member. The first argument
is the object (or the name of a class, for a static member) and the second
argument is the name of the field. The return value is converted to a suitable
Jess value exactly as for call.
(get-reset-globals)
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
None
-
Returns:
-
Boolean
-
Description:
-
Indicates the current setting of global variable reset behavior. See set-reset-globals
for an explanation of this property.
get-salience-evaluation
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
None
-
Returns:
-
Atom
-
Description:
-
Indicates the current setting of salience evaluation behavior. See set-salience-evaluation
for an explanation of this property.
(gensym*)
-
Package:
-
(built-in)
-
Arguments:
-
None
-
Returns:
-
Atom
-
Description:
-
Returns an atom which consists of the letters gen plus an
integer. Use setgen to set the value of
the integer to be used by the next gensym call. Note that, unlike in
CLIPS, these symbols are not guaranteed to be unique. This will
change in a future release.
(get-var <lexeme-expression>)
-
Package:
-
(built-in)
-
Arguments:
-
A string or atom
-
Returns:
-
(Varies)
-
Description:
-
Fetches the value of a variable, given the name of the variable as a string
or atom. (Rarely needed, but when you need it, you'll know.) Most
commonly, get-var is used to fetch the value of a global variable on
the LHS of a rule.
(halt)
-
Package:
-
(built-in)
-
Arguments:
-
None
-
Returns:
-
TRUE
-
Description:
-
Halts rule execution. No effect unless called from the RHS of a rule.
(if <expression> then <action>* [else <action>*])
-
Package:
-
(built-in)
-
Arguments:
-
A Boolean variable or function call returning Boolean, the atom then,
and any number of additional expressions; optionally followed by the atom
else another list of expression.
-
Returns:
-
(Varies)
-
Description:
-
Allows conditional execution of a group of actions. The boolean expression
is evaluated. If it does not evaluate to FALSE, the first list
of expressions is evaluated, and the return value is that returned by the
last expression of that list. If it does evaluate to FALSE, and
the optional second list of expressions is supplied, those expressions
are evaluated and the value of the last is returned.
-
Example:
(if (> ?x 100)
then
(printout t "X is big" crlf)
else
(printout t "X is small" crlf))
(implode$ <multifield-expression>)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
One multifield
-
Returns:
-
String
-
Description:
-
Creates a string from a multifield value. Converts each element of the
multifield to a string, and returns these strings concatenated with single
intervening spaces.
(insert$ <multifield-expression> <integer-expression> <single-or-multifield-expression>+)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
A multifield, an integer, and one more more multifields
-
Returns:
-
A multifield
-
Description:
-
Inserts one or more values in a multifield. Inserts the elements of the
one or more multifields so that they appear starting at the given 1-based
index of the first multifield.
(integer <numeric-expression>)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
One numeric expression
-
Returns:
-
Integer
-
Description:
-
Converts its only argument to an integer. Truncates any fractional component
of the value of the given numeric expression and returns the integral part.
(integerp <expression>)
-
Package:
-
jess.PredFunctions
-
Arguments:
-
One expression
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE for integers; otherwise, returns FALSE.
(intersection$ <multifield-expression> <multifield-expression>)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
Two multifields
-
Returns:
-
Multifield
-
Description:
-
Returns the intersection of two multifields. Returns a multifield consisting
of the elements the two argument multifields have in common.
(jess-version-number)
-
Package:
-
(built-in)
-
Arguments:
-
None
-
Returns:
-
Float
-
Description:
-
Returns a version number for Jess; currently 4.5 .
(jess-version-string)
-
Package:
-
(built-in)
-
Arguments:
-
None
-
Returns:
-
String
-
Description:
-
Returns a human-readable string descriptive of this version of Jess.
(length$ <multifield-expression>)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
Multifield
-
Returns:
-
Integer
-
Description:
-
Returns the number of fields in a multifield value.
(lexemep <expression>)
-
Package:
-
jess.PredFunctions
-
Arguments:
-
Any expression
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE for symbols and strings; otherwise, returns FALSE.
(list-function$)
-
Package:
-
(built-in)
-
Arguments:
-
None
-
Returns:
-
Multifield
-
Description:
-
Returns a multifield list of all the functions currently callable, including
intrinsics, deffunctions, and Userfunctions. Each function name is an atom.
The names are sorted in alphabetical order.
(load-facts <file-name>)
-
Package:
-
(built-in)
-
Arguments:
-
A string or atom representing the name of a file of facts
-
Returns:
-
Boolean
-
Description:
-
Asserts facts loaded from a file. The argument should name a file containing
a list of facts (not deffacts constructs, and no other commands or constructs).
Jess will parse the file and assert each fact. The return value is the
return value of assert when asserting the last fact. In an applet, load-facts
will use getDocumentBase() to find the named file.
Note: See the batch command for a
discussion about specifying filenames in Jess.
(load-function <class-name>)
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
One string or atom representing the name of a Java class
-
Returns:
-
Boolean
-
Description:
-
The argument must be the fully-qualified name of a Java class that implements
the Userfunction interface. The class is loaded in to Jess and added to
the engine, thus making the corresponding command available. See
Extending Jess with Java for more
information.
(load-package <class-name>)
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
One string or atom, the name of a Java class
-
Returns:
-
Boolean
-
Description:
-
The argument must be the fully-qualified name of a Java class that implements
the Userpackage interface. The class is loaded in to Jess and added to
the engine, thus making the corresponding package of commands available.
See Extending Jess with Java for more information.
(log <numeric-expression>)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
One numeric expression
-
Returns:
-
Number
-
Description:
-
Returns the logarithm base e of its only argument.
(log10 <numeric-expression>)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
One numeric expression
-
Returns:
-
Number
-
Description:
-
Returns the logarithm base-10 of its only argument.
(lowcase <lexeme-expression>)
-
Package:
-
jess.StringFunctions
-
Arguments:
-
One atom or string.
-
Returns:
-
String
-
Description:
-
Converts uppercase characters in a string or symbol to lowercase. Returns
the argument as an all-lowercase string.
(max <numeric-expression>+)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
One or more numerical expressions
-
Returns:
-
Number
-
Description:
-
Returns the value of its largest numeric argument
(member$ <single-field-expression> <multifield-expression>)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
A value and a multifield
-
Returns:
-
Integer or FALSE
-
Description:
-
Returns the position (1-based index) of a single-field value within a multifield
value; otherwise, returns FALSE.
(min <numeric-expression>+)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
One or more numeric expressions
-
Returns:
-
Number
-
Description:
-
Returns the value of its smallest numeric argument.
(mod <numeric-expression> <numeric-expression>)
-
Package:
-
(built-in)
-
Arguments:
-
Two integer expressions
-
Returns:
-
Integer
-
Description:
-
Returns the remainder of the result of dividing the first argument by its
second (assuming that the result of the division must be an integer).
(modify <fact-specified> <RHS-slot>*)
-
Package:
-
(built-in)
-
Arguments:
-
A fact-ID and zero or more two-element lists
-
Returns:
-
Fact-ID
-
Description:
-
Modifies the deftemplate fact in the fact list. The fact-ID must belong
to an unordered fact. Each list is taken as the name of a slot in this
fact and a new value to assign to the slot. A new fact is asserted which
is similar to the given fact but which has the specified slots replaced
with new values. The original fact is retracted. The fact-ID of the new
fact is returned. Modifying a definstance fact will cause the
appropriate object properties to be set as well.
(multifieldp <expression>)
-
Package:
-
jess.PredFunctions
-
Arguments:
-
Any value
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE for multifield values; otherwise, returns FALSE.
(neq <expression> <expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
Two or more values
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE if the first argument is not equal in type and value
to all subsequent arguments (see eq).
(new <string-expression> <new-arguments>+)
-
Package:
-
jess.reflect.ReflectFunctions
-
Arguments:
-
A string and one or more arguments
-
Returns:
-
Boolean
-
Description:
-
Creates a new Java object and returns an EXTERNAL_ADDRESS value
containing it. The first argument is the fully-qualified class name: java.util.Vector,
for example. The second and later arguments are constructor arguments.
The constructor will be chosen from among all constuctors for the named
class based on a first-best fit algorithm. Built-in Jess types are
converted as necessary to match available constructors. See the text for
more details.
(not <expression>)
-
Package:
-
(built-in)
-
Arguments:
-
One expression
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE if its only arguments evaluates to FALSE;
otherwise, returns FALSE.
(nth$ <integer-expression> <multifield-expression>)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
A number and a multifield
-
Returns:
-
(Varies)
-
Description:
-
Returns the value of the specified (1-based index) field of a multifield
value.
(numberp <expression>)
-
Package:
-
jess.PredFunctions
-
Arguments:
-
One expression
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE for numbers; otherwise, returns FALSE.
(oddp <integer-expression>)
-
Package:
-
jess.PredFunctions
-
Arguments:
-
One integer expression
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE for odd numbers; otherwise, returns FALSE;
see evenp.
(open <file-name> <router-identifier> ["r"|"w"|"a"])
-
Package:
-
(built-in)
-
Arguments:
-
A file name, an identifier for the file (an atom), and optionally a mode
string: one of r, w, a.
-
Returns:
-
The file identifier
-
Description:
-
Opens a file. Subsequently, the given router identifier can be passed to
printout, read, readline, or any other functions
that accept I/O routers as arguments. By default, the file is opened for
reading; if a mode string is given, it may be opened for reading only (r),
writing only (w), or appending (a).
Note: See the batch command for a
discussion about specifying filenames in Jess.
(or <expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
One or more expressions
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE if any of the arguments evaluates to a non-FALSE
value; otherwise, returns FALSE.
(pi)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
None
-
Returns:
-
Number
-
Description:
-
Returns the number pi.
(ppdefrule <rule-name>)
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
A string or atom representing the name of a rule
-
Returns:
-
String containing rule's text
-
Description:
-
Displays the text of a given rule in a pretty-print representation.
(printout <router-identifier> <expression>*)
-
Package:
-
(built-in)
-
Arguments:
-
A router identifier followed by zero or more expressions
-
Returns:
-
nil
-
Description:
-
Sends unformatted output to the specified logical name. Prints its arguments
to the named router, which must be open for output. No spaces are added
between arguments. The special atom crlf prints as a newline.
The special router name t can be used to signify standard output.
(random)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
None
-
Returns:
-
Number
-
Description:
-
Returns a pseudo-random integer between 0 and 65536.
(read [<router-identifier>])
-
Package:
-
(built-in)
-
Arguments:
-
An optional input router identifier (when omitted t is the default)
-
Returns:
-
(Varies)
-
Description:
-
Reads a single-field value from a specified logical name. Read a single
atom, string, or number from the named router, returns this value. The
router t means standard input. Newlines are treated as ordinary
whitespace; this behaviour is different than in CLIPS, which returns newlines
as tokens. If you need to parse text line-by-line, use readline
and explode$.
(readline [<router-identifier>])
-
Package:
-
(built-in)
-
Arguments:
-
An optional input router identifier (when omitted t is the default)
-
Returns:
-
String
-
Description:
-
Reads an entire line as a string from the specified logical name (router).
The router t means standard input.
(replace$ <multifield-expression> <begin-integer-expression>
<end-integer-expression> <single-or-multifield-expression>+)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
A multifield, two numeric expressions, and one or more multifields
-
Returns:
-
Multifield
-
Description:
-
Replaces the specified range of a multifield value with a set of values.
The last one more more multifields are inserted into the first multifield,
replacing elements between the 1-based indices given by the two numeric
arguments, inclusive.
-
Example:
Jess> (replace$ (create$ a b c) 2 2 (create$ x y z))
(a x y z c)
(reset)
-
Package:
-
(built-in)
-
Arguments:
-
None
-
Returns:
-
TRUE
-
Description:
-
Removes all facts from the fact list, removes all activations, then asserts
the fact (initial-fact), then asserts all facts found in
deffacts, asserts a fact representing each registered definstance, and
(if the set-reset-globals property is TRUE) initializes all
defglobals.
(rest$ <multifield-expression>)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
One multifield
-
Returns:
-
Multifield
-
Description:
-
Returns all but the first field of a multifield as a new multifield.
(retract <integer-expression>+)
-
Package:
-
(built-in)
-
Arguments:
-
One or more fact-IDs
-
Returns:
-
TRUE
-
Description:
-
Retracts the facts whose IDs are given. Retracting a definstance fact will
result in an implict call to undefinstance for the corresponding object
(the object will no longer be pattern-matched).
(return [<expression>])
-
Package:
-
(built-in)
-
Arguments:
-
An optional expression
-
Returns:
-
(Varies)
-
Description:
-
Returns the given value from a deffunction. Exits the deffunction immediately.
(round <numeric-expression>)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
One numeric expression
-
Returns:
-
Integer
-
Description:
-
Rounds its argument toward the closest integer or negative infinity if
exactly between two integers.
(rules)
-
Package:
-
(built-in)
-
Arguments:
-
None
-
Returns:
-
TRUE
-
Description:
-
Prints a list of all defrules.
(run)
-
Package:
-
(built-in)
-
Arguments:
-
None
-
Returns:
-
TRUE
-
Description:
-
Starts the inference engine. Jess will keep running until no more activations
remain or halt is called.
(save-facts <file-name> [<deftemplate-name>])
-
Package:
-
(built-in)
-
Arguments:
-
A filename, and optionally an atom
-
Returns:
-
Boolean
-
Description:
-
Saves facts to a file. Attempts to open the named file for writing, and
then writes a list of all facts on the fact list to the file. This file
is suitable for reading with load-facts. If the optional second argument
is given, only facts whose head matches this atom will be saved. Does not
work in applets.
Note: See the batch command for a
discussion about specifying filenames in Jess.
(set <external-address> <string-expression> <expression>)
-
Package:
-
jess.reflect.ReflectFunctions
-
Arguments:
-
An external address, a string, and an expression
-
Returns:
-
The last argument
-
Description:
-
Sets a Java Bean's property to the given value. The first argument is the
Bean object; the second argument is the name of the property. The third
value is the new value for the property; the same conversions are applied
as for new and call.
(set-member (<external-address> | <string-expression>)
<string> <expression>+)
-
Package: jess.reflect.ReflectFunctions
-
Arguments:
-
An external address or a string, a string, and one or more expressions
-
Returns:
-
The last argument
-
Description:
-
Sets a Java object's member variable to the given value. The first argument
is the object (or the name of the class, in the case of a static member
variable). The second argument is the name of the variable. The third value
is the new value for the variable; the same conversions are applied as
for new and call.
(set-reset-globals (TRUE | FALSE | nil))
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
One boolean value (TRUE or FALSE or nil)
-
Returns:
-
Boolean
-
Description:
-
Changes the current setting of the global variable reset behavior. If this
property is set to TRUE (the default), then the (reset) command reinitializes
the values of global variables to their initial values (if the initial
value was a function call, the function call is reexecuted.) If the property
is set to FALSE or nil, then (reset) will not affect global
variables. Note that in previous versions of Jess, defglobals were
always reset; but if the initial value was set with a fucntion call,
the function was not reevaluated. Now it is.
(set-salience-evaluation (when-defined | when-activated | every-cycle))
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
One of the atoms when-defined, when-activated, or every-cycle
-
Returns:
-
One of the potential arguments (the previous value of this property)
-
Description:
-
Changes the current setting of the salience evaluation behavior. By default,
a rule's salience will be determined once, when the rule is defined (when-defined.)
If this property is set to when-activated, then the salience of each rule
will be redetermined immediately before each time it is placed on the agenda.
If the property is set to every-cycle, then the salience of every rule
is redetermined immediately after each time any rule fires.
(set-strategy (depth | breadth))
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
An atom or string representing the name of a strategy (can be a fully-qualified
Java class name). You can use depth and breadth to
represent the two built-in strategies.
-
Returns:
-
The previous strategy as an atom
-
Description:
-
Lets you specify the conflict resolution strategy Jess uses to order
the firing of rules of equal salience. Currently, there are two strategies
available: depth (LIFO) and breadth (FIFO). When the
depth strategy is in effect (the default), more recently activated rules
are fired before less recently activated rules of the same salience. When
the breadth strategy is active, rules of the same salience fire in the
order in which they are activated. Note that in either case, if several
rules are activated simultaneously (i.e., by the same fact-assertion event)
the order in which these several rules fire is unspecified, implementation-dependent
and subject to change. More built-in strategies may be added in the future.
You can (perhaps) implement your own strategies in Java by creating a class
that implements the jess.Strategy interface and then specifying
its fully-qualified classname as the argument to set-strategy.
Details can be gleaned from the source. At this time, though, I think
some of the methods you'd need to call are package-protected.
(setgen <numeric-expression>)
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
A numeric expression
-
Returns:
-
TRUE
-
Description:
-
Sets the starting number used by gensym*. Note that if this number
has already been used, gensym* uses the next larger number that
has not been used.
(socket <Internet-hostname> <TCP-port-number> <router-identifier>)
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
An Internet hostname, a TCP port number, and a router identifier
-
Returns:
-
The router identifier
-
Description:
-
Somewhat equivalent to open, except that instead
of opening a file, opens an unbuffered TCP network connection to the named
host at the named port, and installs it as a pair of read and write routers
under the given name.
(sqrt <numeric-expression>)
-
Package:
-
jess.MathFunctions
-
Arguments:
-
A numeric expression
-
Returns:
-
Number
-
Description:
-
Returns the square root of its only argument.
(store <string or atom> <expression>)
-
Package:
-
(built-in)
-
Arguments:
-
A string or atom and any other value
-
Returns:
-
(varies)
-
Description:
-
Associates the expression with the name given by the first argument,
such that later calls to the fetch will retrieve
it. Analagous to the store() member function of the
jess.Rete class. See section on
Using store and fetch for more
details.
(str-cat <expression>*)
-
Package:
-
jess.StringFunctions
-
Arguments:
-
Zero or more expressions
-
Returns:
-
String
-
Description:
-
Concatenates its arguments as strings to form a single string.
(str-compare <string-expression> <string-expression>)
-
Package:
-
jess.StringFunctions
-
Arguments:
-
Two strings
-
Returns:
-
Integer
-
Description:
-
Lexicographically compares two strings. Returns 0 if the strings are identical,
a negative integer if the first is lexicographically less than the second,
a positive integer if lexicographically greater.
(str-index <lexeme-expression> <lexeme-expression>)
-
Package:
-
jess.StringFunctions
-
Arguments:
-
Two atoms
-
Returns:
-
Integer or FALSE
-
Description:
-
Returns the position of the first argument within the second argument.
This is the 1-based index at which the first string first appears in the
second; otherwise, returns FALSE.
(str-length <lexeme-expression>)
-
Package:
-
jess.StringFunctions
-
Arguments:
-
An atom
-
Returns:
-
Integer
-
Description:
-
Returns the length of an atom in characters.
(stringp <expression>)
-
Package:
-
jess.PredFunctions
-
Arguments:
-
One expression
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE for strings; otherwise, returns FALSE.
(sub-string <begin-integer-expression> <end-integer-expression>
<string-expression>)
-
Package:
-
jess.StringFunctions
-
Arguments:
-
Two numbers and a string
-
Returns:
-
String
-
Description:
-
Retrieves a subportion from a string. Returns the string consisting of
the characters between the two 1-based indices of the given string, inclusive.
(subseq$ <multifield-expression> <begin-integer-expression> <end-integer-expression>)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
A multifield and two numeric expressions
-
Returns:
-
Multifield
-
Description:
-
Extracts the specified range from a multifield value consisting of the
elements between the two 1-based indices of the given multifield, inclusive.
(subsetp <multifield-expression> <multifield-expression>)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
Two multifields
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE if the first argument is a subset of the second (i.e.,
all the elements of the first multifield appear in the second multifield);
otherwise, returns FALSE.
(sym-cat <expression>*)
-
Package:
-
(built-in)
-
Arguments:
-
Zero or more expressions
-
Returns:
-
Atom
-
Description:
-
Concatenates its arguments as strings to form a single symbol.
(symbolp <expression>)
-
Package:
-
jess.PredFunctions
-
Arguments:
-
One expression
-
Returns:
-
Boolean
-
Description:
-
Returns TRUE for symbols; otherwise, returns FALSE.
(system <lexeme-expression>*)
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
Zero or more atoms
-
Returns:
-
TRUE
-
Description:
-
Appends its arguments to form a command which is then sent to the operating
system. Executes the operating-system command-line constructed by converting
each argument to a string. Normally blocks (i.e., Jess stops until the
applet returns), but if the last argument is an ampersand (&),
the program will run in the background.
(time)
-
Package:
-
jess.MiscFunctions
-
Arguments:
-
None
-
Returns:
-
Number
-
Description:
-
Returns the number of seconds since 12:00 AM, Jan 1, 1970.
(try <expression>* catch <expression>*)
-
Package:
-
(built-in)
-
Arguments:
-
One or more expressions, followed by the atom catch, followed
by zero or more expressions
-
Returns:
-
(Varies)
-
Description:
-
This command works something like Java try with a few simplifications.
The biggest difference is that the catch clause can specify neither
a type of exception nor a variable to receive the exception object. All
exceptions occurring in a try block are routed to the single required
catch block. The variable ?ERROR is made to point to
the exception object as an EXTERNAL_ADDRESS. For example:
-
(try
-
(open NoSuchFile.txt r)
-
catch
(printout t (call ?ERROR toString) crlf))
prints
Rete Exception in routine _open::call.
Message: I/O Exception java.io.FileNotFoundException: NoSuchFile.txt.
An empty catch block is fine. It just signifies ignoring possible
errors.
(undefrule <rule-name>)
-
Package:
-
(built-in)
-
Arguments:
-
An atom representing the name of a rule
-
Returns:
-
Boolean
-
Description:
-
Deletes a defrule. Removes the named rule from the Rete network and returns
TRUE if the rule existed.. This rule will never fire again.
(union$ <multifield-expression> <multifield-expression>)
-
Package:
-
jess.MultiFunctions
-
Arguments:
-
Two multifields
-
Returns:
-
Multifield
-
Description:
-
Returns a new multifield consisting of the union of its two multifield
arguments (i.e., of all the elements that appear in the two arguments with
duplicates removed).
(unwatch <watch-item>)
-
Package:
-
(built-in)
-
Arguments:
-
One of the atoms all, rules, compilations, activations,
facts
-
Returns:
-
TRUE
-
Description:
-
Causes trace output to not be printed for the given indicator. See watch.
(upcase <lexeme-expression>)
-
Package:
-
jess.StringFunctions
-
Arguments:
-
A string or atom
-
Returns:
-
A string
-
Description:
-
Converts lowercase characters in a string or symbol to uppercase. Returns
the argument as an all-uppercase string.
(view)
-
Package:
-
jess.view.ViewFunctions
-
Arguments:
-
None
-
Returns:
-
TRUE
-
Description:
-
This Userfunction is included in the Jess distribution but is not normally
installed. It requires Java 1.1. You must load it using load-package
(the class name is jess.view.ViewFunctions). When invoked, it
displays a live snapshot of the Rete network in a graphical window. See
How Jess Works.
(watch (all | rules | compilations | activations | facts))
-
Package:
-
(built-in)
-
Arguments:
-
One of the atoms all, rules, compilations, activations,
facts
-
Returns:
-
TRUE
-
Description:
-
Produces additional debug output when specific events happen in Jess, depending
on the argument. Any number of different watches can be active simultaneously:
-
rules: prints a message when any rule fires.
-
compilations: prints a message when any rule is compiled.
-
activations: prints a message when any rule is activated, or deactivated,
showing which facts have caused the event.
-
facts: print a message whenever a fact is asserted or retracted.
-
all: all of the above.
(while <expression> [do] <action>*)
-
Package:
-
(built-in)
-
Arguments:
-
A Boolean value or a function call returning Boolean, the atom do,
and zero or more expressions
-
Returns:
-
(Varies)
-
Description:
-
Allows conditional looping. Evaluates the boolean expression repeatedly.
As long as it does not equal FALSE, the list of other expressions
are evaluated. The value of the last expression evaluated is the return
value.
4 Writing Jess Code
Many useful expert systems can be written using only the Jess language
as presented above. I won't present a tutorial on writing such systems here
(maybe someday!), but I do want to share a few useful hints and ideas.
4.1 Using an External Editor
Jess allows you to enter rules directly at its interactive prompt. While
this is fine for experimenting, Jess doesn't yet have the ability to remember
the source text for all the rules and constructs you enter. Therefore,
you will typically enter your rules and other data into a separate script
file and read it into Jess using the batch command. Jess does
offer the ppdefrule and save-facts commands, both of
which can be very helpful in interactively building up a system definition
and them storing it in a file. And as described in a previous section,
you can use the system command to start the external editor form
within Jess, if desired.
4.2 Efficiency
The single biggest determinant of Jess performance is the number of partial
matches generated by your rules. You should always try to obey the
following (sometimes contradictory) guidelines while writing your rules:
-
Put the most specific patterns (those that will match the fewest
facts) near the top of each rule's LHS.
-
Put the most transient patterns (those that will match facts that
are frequently retracted and asserted) near the bottom of a LHS.
You can use the view command to find out how
many partial matches your rules generate. See How
Jess Works.
4.3 Error Reporting and Debugging
I've tried hard to improve Jess's syntax error reporting in this release,
but it is still not as detailed as it could be. When you get an error from
Jess (during parsing or at runtime) it is generally delivered as a Java
exception. The exception will contain an explanation of the problem and
the stack trace of the exception will help you understand what went wrong.
For this reason, it is very important that, if you're embedding
Jess in a Java application, you don't write code like this:
try
{
Rete engine;
...
engine.executeCommand("(gibberish!)");
}
catch (ReteException re) { /* ignore errors */ }
If you ignore the Java exceptions, you will miss Jess's explanations
of what's wrong with your code. Don't laugh - more people code this
way than you'd think!
Anyway, if you attempt to load the folowing rule in the standard Jess
command-line executable,
Jess> (defrule foo-1
(foo bar)
->
(printout "Found Foo Bar" crlf))
You'll get the following printout:
Rete Exception in routine Jesp::parseDefrule.
Message: Expected '=>' at line 2: ( defrule foo-1 ( foo bar ) -> .
at jess.Jesp.parseError(Compiled Code)
at jess.Jesp.doParseDefrule(Compiled Code)
at jess.Jesp.parseDefrule(Compiled Code)
at jess.Jesp.parseSexp(Compiled Code)
at jess.Jesp.parse(Compiled Code)
at jess.Main.main(Compiled Code)
Looking at the routine names listed in the stack trace make it fairly clear
that a defrule was being parsed, and the detail message explains
that the position of the . was reached in the input without finding
the expected => symbol (we accidentally typed -> instead).
Runtime errors can be more puzzling, but the stack trace will give you
a lot of information. Here's a rule where we erroneously try to add the
number 3.0 to the word four:
Jess> (defrule foo-2
=>
(printout t (+ 3.0 four) crlf))
When we (reset) and (run) we'll see:
Rete Exception in routine Value::intValue while executing defrule foo-2.
Message: Not a number: four type = 1 at line 8: ( run ) .
at jess.Value.typeError(Compiled Code)
at jess.Value.numericValue(Compiled Code)
at jess.Plus.call(Compiled Code)
at jess.Funcall.simpleExecute(Compiled Code)
at jess.Funcall.execute(Compiled Code)
at jess.Funcall.execute(Compiled Code)
at jess.Funcall.execute(Compiled Code)
at jess.Defrule.fire(Compiled Code)
at jess.Activation.fire(Compiled Code)
at jess.Rete.run(Compiled Code)
at jess.Rete.run(Compiled Code)
at jess.HaltEtc.call(Compiled Code)
at jess.Funcall.simpleExecute(Compiled Code)
at jess.Funcall.execute(Compiled Code)
at jess.Funcall.execute(Compiled Code)
at jess.Jesp.parseAndExecuteFuncall(Compiled Code)
at jess.Jesp.parseSexp(Compiled Code)
at jess.Jesp.parse(Compiled Code)
at jess.Main.main(Compiled Code)
In this case, the error message is pretty clear except for the claim that
the offending statement is run. To find out what was really happening,
we have to look at the stack trace. Starting from the top down, we find
Value.numericValue() was called by Plus.call(). A few
levels down, we see Defrule.fire(). Taken together, this means
that an addition operation on the RHS of the rule foo-2 (from the
first line of the trace) found the symbol four
as one of its operands when it expected a number.
The notation type = 1 in the error message, by the way, refers
to a set of constants in the class jess.RU. The values of these
constants are presented in section 5.4.1, The class
jess.Value. Consulting that table, we see that type 1 is
RU.ATOM, a symbol, which is indeed not a number.
If we make a similar mistake on the LHS of a rule:
Jess> (defrule foo-3
(test (eq 3 (+ 2 one)))
=>
)
We see the following after a reset:
Rete Exception in routine Value::intValue while executing 'test' CE:
[NodeTest ntests=1 [Test1: test=EQ;slot_idx=3;sub_idx=-1;
slot_value=eq 3 + 2 one] ;usecount = 1].
Message: Not a number: one type = 1 at line 11: ( reset ) .
at jess.Value.typeError(Compiled Code)
at jess.Value.numericValue(Compiled Code)
at jess.Plus.call(Compiled Code)
at jess.Funcall.simpleExecute(Compiled Code)
at jess.Funcall.execute(Compiled Code)
at jess.Funcall.execute(Compiled Code)
at jess.Funcall.execute(Compiled Code)
at jess.NodeTest.runTests(Compiled Code)
at jess.NodeTest.callNode(Compiled Code)
at jess.Node.passAlong(Compiled Code)
at jess.Node1TELN.callNode(Compiled Code)
at jess.Node.passAlong(Compiled Code)
at jess.Node1TECT.callNode(Compiled Code)
at jess.Rete.processTokenOneNode(Compiled Code)
at jess.Rete.updateNodes(Compiled Code)
at jess.ReteCompiler.addRule(Compiled Code)
at jess.Rete.addDefrule(Compiled Code)
at jess.Jesp.doParseDefrule(Compiled Code)
at jess.Jesp.parseDefrule(Compiled Code)
at jess.Jesp.parseSexp(Compiled Code)
at jess.Jesp.parse(Compiled Code)
at jess.Main.main(Compiled Code)
Again, the error message is somewhat but not completely helpful (it
says the error was in the function reset, but it also says
that a (test) CE was being executed, and it prints out a stylized
version of the test CE itself) and the
stack trace contains additional information. Here we see our old friends
Value.numericValue() and Plus.call() being called, but
we don't see the Defrule being fired. Instead we see lots of oddly named
classes and functions with names containing Node and Token.
This is always a tip-off that the error happened on Defrule LHS
processing, as are both the fact that the error happened during a
reset and the message at the top of the trace. Way down the stack we
see Rete.assert() being called by Rete.reset(),
which also indicates that LHS processing was in progress when the
exception happened.
The funny string starting with [NodeTest ntests=1; ... is
Jess's internal representation for a single node in the Rete network
that encodes the test CE from the rule above. Looking at it,
you can see that it includes the function call (+ 2 one),
which should help you track it down. Note that in this case, Jess
can't tell you which rule this node belongs to, as it
theoretically could be shared by several rules (see
How Jess Works for details.)
5 Embedding Jess in a Java Program
There are three different ways to use Jess and Java code together:
-
You can embed Jess in your own Java applications. This is covered in this
Section of the manual.
-
You can write Java classes which you can add to Jess so that they will
appear as a part of the Jess language. This is discussed in Extending
Jess With Commands Written in Java.
-
You can manipulate Java objects directly from the Jess language using the
optional jess.reflect package (the functions new, call,
set, get, set-member, get-member, defclass,
and definstance). This is covered in Accessing
Java Objects Directly From Jess.
5.1 The jess.Rete Class
5.1.1 Executing a File of Jess Code
Using Jess from Java code is simple. The jess.Rete class contains
the expert system engine. The jess.Jesp class contains the Jess
parser. To execute a file of CLIPS code in Jess (like the Jess batch
command), simply create a Rete object and a Jesp object,
tell the Jesp object about the file, and call Jesp.parse(boolean
prompt):
import jess.*;
// ...
// See info about the ReteDisplay classes below
NullDisplay nd = new NullDisplay();
// Create a Jess engine
Rete rete = new Rete(nd);
// Open the file test.clp
FileInputStream fis = new FileInputStream("test.clp");
// Create a parser for the file, telling it where to take input
// from and which engine to send the results to
Jesp j = new Jesp(fis, rete);
do
{
try
{
// parse and execute one construct, without printing a prompt
j.parse(false);
}
catch (ReteException re)
{
// All Jess errors are reported as 'ReteException's.
re.printStackTrace(nd.stderr());
}
} while (fis.available() > 0);
Note that if the file test.clp contains the CLIPS reset
and run commands, the Jess engine will run to completion during
the parse() calls. Also note that Jess will throw
jess.ReteException exceptions to signal errors.
5.1.2 Adding Optional Commands
The code above will create only a minimal Jess engine, including a relatively
small number of functions (the intrinsic functions). Many of the
functions packaged with Jess are actually implemented as optional jess.Userpackage
classes. You can choose to load some or all of these into your Jess applications.
Omitting unneeded functions may be especially important in applets, where
a small footprint is important.
Here is a snippet of code (from jess/Main.java) which will
load all the standard optional functions, without causing an error if any
of them are missing:
// Load in optional packages, but don't fail if any are missing.
Rete rete = new Rete(nd);
String [] packages = { "jess.StringFunctions",
"jess.PredFunctions",
"jess.MultiFunctions",
"jess.MiscFunctions",
"jess.MathFunctions",
"jess.BagFunctions",
"jess.reflect.ReflectFunctions",
"jess.view.ViewFunctions" };
for(int i=0; i< packages.length; i++)
{
try
{
rete.addUserpackage((Userpackage)
Class.forName(packages[i]).newInstance());
}
catch (Throwable t) { /* Optional package not present */ }
}
To find out which functions are built into Jess and which are optional,
see the Jess Function Guide, which lists the
package in which each function appears.
5.1.3 Executing Individual Commands
For somewhat more control over Jess from your Java program, you can use
the Rete class's executeCommand(String cmd) method. For
example, after the above code, you could include the following:
try
{
rete.executeCommand("(reset)");
rete.executeCommand("(assert (foo bar foo))");
rete.executeCommand("(run)");
// Prints '42'
System.out.println(rete.executeCommand("(+ 37 5)"));
}
catch (ReteException ex)
{
System.err.println("Foo bar error.");
}
Commands executed via executeCommand() may refer to Jess variables;
they will be interpreted in the global context. In general, only
defglobals can be used in this way.
5.1.4 Other Methods in the Rete Class
Rete has a number of other public methods which I have not documented
here; see the source for details. There are functions to assert and retract
facts (which you are invited to use) and functions to find and remove various
constructs (which you may also use.) There are also functions which allow
you to add constructs (defrules, deftemplates, etc.) In general, it is
best that you not use these in your programs, because they are highly
subject to change in future Jess versions, as are the details of the classes
like jess.Defrule which represent constructs. Two other functions
you will certainly use are addUserfunction() and addUserpackage().
These are explained in Extending Jess With Commands
Written in Java. In addition, the methods run(), reset(),
and halt() are useful.
There is a set of diagnostic methods in Rete which return Enumerations
of various data structures in the engine:
-
public synchronized Enumeration listDeftemplates()
-
public synchronized Enumeration listDefrules()
-
public synchronized Enumeration listFacts()
-
public synchronized Enumeration listActivations()
-
public synchronized Enumeration listDefglobals()
-
public synchronized Enumeration listUserfunctions()
Feel free to use these for debugging purposes, but don't get too chummy
with them: they are subject to change without notice, especially the types
of the objects they return. Note that listUserfunctions()
returns an Enumeration of all the Jess functions in the engine,
including deffunctions and intrinsics.
5.2 The ReteDisplay Interface.
There is a lot you can do with just what we've discussed so far. One thing
that you might not like, though, is that by default, programs we write
using the above techniques will send their output to your Java program's
System.out and take input from System.in. If you're writing
a graphical program, this is clearly not what you want. Jess provides an
interface jess.ReteDisplay that lets you deal with
this in a simple way. ReteDisplay also provides some hooks into
the engine's internal workings. The ReteDisplay interface provides
two types of functions:
-
Functions that return the intial input, output, and error streams that
the engine should use.
-
Functions that are called by the engine whenever an event occurs (events
here meaning that a construct is parsed, fact is asserted, rule is activated,
and so forth).
5.2.1 Using ReteDisplay
Every Rete object must be constructed with an instance of ReteDisplay.
Here is the definition of the ReteDisplay interface:
public interface ReteDisplay
{
// Rete sets its initial input/output using these
// These must be implemented
public java.io.PrintStream stdout();
public java.io.InputStream stdin();
public java.io.PrintStream stderr();
// These notify the ReteDisplay object when things happen
// Can do nothing if you want!
public void assertFact(ValueVector fact);
public void retractFact(ValueVector fact);
public void addDeffacts(Deffacts df);
public void addDeftemplate(Deftemplate dt);
public void addDefrule(Defrule rule);
public void activateRule(Defrule rule);
public void deactivateRule(Defrule rule);
public void fireRule(Defrule rule);
// This gives the Rete object access to Applet resources
// Should return null if not in an Applet
public java.applet.Applet applet();
}
Jess ships with two different classes that implement this interface. You
can write your own or modify these for use in your own applications.
-
The class jess.NullDisplay, which implements part of the Jess
command-line interface, uses the notification functions as a means of notifying
Java Observers of changes to the Rete network (NullDisplay extends
java.util.Observable). This is an important part of the implementation
of the view command, which can display the Rete network architecture
interactively as rules are added, which can be a valuable debugging tool.
jess.NullDisplay simply routes Java's standard output and standard
input to the Rete engine. It is convenient to use for many applications
because it has no GUI. We used it above in our simple examples, and the
standard Jess command line driver jess.Main uses it as well.
-
The class jess.ConsoleDisplay is more complex. It does everything
jess.NullDisplay does, and more. It is a graphical display, routing
Jess input and output into graphical text boxes. It uses the notification
functions to support the view command just as NullDisplay does.
The jess.Console and jess.ConsoleApplet classes are examples
of programs that use jess.ConsoleDisplay. See
Jess45/examples/console.html for an example Web page embedding
jess.ConsoleApplet. These classes should give you a good idea
of how to use ReteDisplay to embed Jess in a GUI application.
5.2.2 The jess.TextAreaOutputStream and jess.TextInputStream
Classes
Jess ships with two utility classes that are very useful when building
GUIs for Jess: the jess.TextAreaOutputStream and jess.TextInputStream
classes. Both can serve as adapters between Jess and graphical input/output
widgets. The TextAreaOutputStream class is, as the name implies, a Java
OutputStream that sends any data written to it to a java.awt.TextArea.
This lets you place Jess's output in a scrolling window on your GUI. The
jess.Console and jess.ConsoleApplet jess GUIs use these
classes. To use TextAreaOutputStream, simply write a class that
implements ReteDisplay, returning a suitably wrapped TextAreaOutputStream
from the stdout() and stderr() methods:
import jess.*;
public class MyDisplay implements ReteDisplay
{
public TextArea ta = new TextArea(20, 80);
TextAreaOutputTream taos = new TextAreaOutputStream(ta);
PrintStream ps = new PrintStream(taos, true);
public MyDisplay()
{
Frame f = new Frame("Jess Demo");
f.add(ta);
f.pack();
f.show();
}
PrintStream stdout() { return ps; }
// ... (Plus all the other ReteDisplay methods)
Now if you construct a jess.Rete object with an instance of this
display, the Jess output will go into a scrolling window. If you have
a source distribution, study jess/ConsoleDisplay.java
and jess/Console.java to see a complete example of this.
jess.TextInputStream is similar, but it is an InputStream
instead. It is actually quite similar to java.io.StringBufferInputStream,
except that you can continually add new text to the end of the stream (using
the appendText() method). It is intended that you create a jess.TextInputStream,
return it from your jess.ReteDisplay.stdin() method, and then
(in an AWT event handler, somewhere) append new input to the stream whenever
it becomes available. See the same jess/Console* files for a complete
usage example for this class as well.
5.2.3 Switching Streams in Mid-Stream
The Rete engine will call the stdout() and stdin() methods
of your ReteDisplay class at least once when it is created.
It is from these return values that the initial definition of the t,
WSTDOUT, and WSTDERR I/O routers are made (see Manipulating
Jess I/O Routers in Java). It will never call these methods again. This
means that changing the return values of these functions over time may
have no effect. To re-route input and output you must use the explicit
router functions.
5.3 Manipulating Jess I/O Routers in Java
ReteDisplay lets you set up the minumal initial state for a Rete
object, but Jess can read from more that just standard input and standard
output. Jess I/O routers can be easily manipulated from Java. These six
functions in the Rete class manipulate the router list:
-
public void addInputRouter(String s, InputStream is, boolean console)
-
public void addOutputRouter(String s, OutputStream os)
-
public void removeInputRouter(String s)
-
public void removeOutputRouter(String s)
-
public InputStream getInputRouter(String s)
-
public OutputStream getOutputRouter(String s)
When Jess starts up, there are one input router and three output routers
defined: the t router, which reads and writes from the standard
input and output; the WSTDOUT router, which Jess uses for all
prompts, diagnostic outputs, and other displays; and the WSTDERR
router, which Jess uses to print stack traces and error messages. You can
reroute these inputs and outputs simply by changing the Input and Output
streams they are attached to using the above functions. You can use any
kind of streams you can dream up: network streams, file streams, etc.
The boolean argument console to the addInputRouter
method specifies whether the stream should be treated like the standard
input or like a file. The difference is that on console-like streams, a
read call consumes an entire line of input, but only the first
token is returned; while on file-like streams, only the characters that
make up each token are consumed on any one call. That means, for instance,
that a read followed by a readline will consume two lines
of text from a console-like stream, but only one from a file-like stream,
given that the first line is of non-zero length. This odd behaviour is
actually just following the behaviour of CLIPS.
The Rete class has two more handy router-related methods: outStream()
and errStream(), both of which return a PrintStream object.
outStream() returns a stream that goes to the same place as the
current setting of WSTDOUT; errStream() does the same
for WSTDERR.
You can add your own routers which do I/O through any Java stream