Updated Feb 27th 2006.
If you have any questions, try finding the answer on the Jess documentation before asking the tutor.
Jess, or the Java Expert System Shell as it was first known, is rather more than the name suggests, comprising a general-purpose programming language which can access all Java classes and libraries.
However, don't let the word "Java" put you off: we will start with the basic constructs fact and rule which are enough to develop simple applications without having to do any procedural programming. Unfortunately the Jess documentation does not take the same approach, and expects you to master the more fiddly stuff before getting to the basics of facts and rules.
Before attempting the exercises at the bottom of this page, you should run all the examples below so you can start getting a feel for how Jess applications work.
If you are working on a lab machine you just need to click on the "jess" shortcut which should be somewhere on the desktop. You will then see a command-line mode window with the prompt:
Jess>
To be sure it's working, enter the following elementary Jess program:
(printout t "Hello World!" crlf)
What do you think crlf means?
(If you have a trial version of Jess installed on your own machine, one way to start it up is to open a command-mode window, cd to the Jess folder and enter java jess.Main or java jess.Console.)
For your first Jess application you need to do the following things:
Here's an example:
(clear) TRUE Jess> (assert(smoke)) <Fact-0> Jess> (defrule fire1 (smoke) => (assert(fire)) ) TRUE Jess> (run) 1 Jess> (facts) f-0 (MAIN::smoke) f-1 (MAIN::fire) For a total of 2 facts. Jess>
The general format of a rule definition is:
(defrule 'name' LHS => RHS) where
Note the use of the assert function in the above example. If the rule looked like this:
(defrule fire1 (smoke) => (fire) )
then Jess would try to execute a function called 'fire' rather than adding the fact (fire) to working memory.
The LHS can be a Boolean combination for example:
We are using the simplest variety of Jess facts here, namely ordered facts, consisting of a list of atoms between parentheses. Later in the course we will look at unordered facts and shadow facts.
Notice that Jess gives each fact a number: 0 is 'smoke', 1 is 'fire'.
You can remove facts from working memory by entering (retract n), where n is the fact's number.
Try running the example above, and then retract the fact 'smoke' and enter (facts) again. What do you notice?
Now clear your working memory and type in the following example.
Jess> (clear) TRUE Jess> (defrule fire2 (logical (smoke)) => (assert(fire)) ) TRUE Jess> (assert(smoke)) <Fact-0> Jess> (run) 1 Jess> (facts) f-0 (MAIN::smoke) f-1 (MAIN::fire) For a total of 2 facts.
Now retract 'smoke' again and enter (facts). Do you notice anything different?
Variables are represented in Jess as ?variable_name where variable_name is a string made up of alphanumeric characters, dashes (-) and underscores (_). Variables are untyped and are usually declared implicitly when they are first given a value. Here's a familiar example:
(reset) TRUE Jess> (assert (man socrates)) <Fact-1> Jess> (defrule mortality (man ?x) => (assert(mortal ?x)) ) TRUE Jess> (run) 1 Jess> (facts) f-0 (MAIN::initial-fact) f-1 (MAIN::man socrates) f-2 (MAIN::mortal socrates) For a total of 3 facts.
Here's an example of an effectively recursive rule:
(defrule anc (or (parent ?a ?b) (and (parent ?a ?c)(ancestor ?c ?b)) ) => (assert(ancestor ?a ?b))) (assert(parent andy betty)) (assert(parent betty charlie)) (assert(parent charlie donna))
Before running this, write out what you think the results should be.
You don't have to type every line of Jess programs at the prompt before you can run them. You can also load them from files using the (batch) command, for example:
Jess> (batch "C:/Jess/Jess61p8/CIS341/socrates.txt")
Note: you should use the forward slash '/' whether you're working on Unix or Windows, and it's generally safer to put double quotes around the path name.
Loading a file like this is equivalent to typing each line manually: nothing will happen until you enter (run) (unless this was in the file of course).
Jess> (deffacts flowers (roses red) (violets blue)) TRUE Jess> (facts) For a total of 0 facts. Jess> (reset) TRUE Jess> (facts) f-0 (MAIN::initial-fact) f-1 (MAIN::roses red) f-2 (MAIN::violets blue) For a total of 3 facts.
(defrule anc (or (parent ?a ?b) (and (parent ?a ?c)(ancestor ?c ?b)) )
=> (assert(ancestor ?a ?b)))
(deffacts family
(parent andy betty)
(parent betty charlie)
(parent betty edna)
(parent charlie donna)
(parent edna freddy))
(defquery search
"Finds ancestor facts with a specified first field"
(declare (variables ?X))
(ancestor ?X ?Y))
There are two complications: first, queries only examine working memory and will not automatically
invoke recursive rule application; secondly the result is returned in a form that still requires some
fiddly Jess coding to extract it. The example below is taken directly from the Jess
documentation, and you should read further in section 2.9.
Jess> (reset)
TRUE
Jess> (run)
11
Jess> (bind ?it (run-query search andy))
<External-Address:java.util.AbstractList$Itr>
Jess> (while (?it hasNext)
(bind ?token (call ?it next))
(bind ?fact (call ?token fact 1))
(bind ?slot (fact-slot-value ?fact __data))
(bind ?datum (nth$ 2 ?slot))
(printout t ?datum crlf))
freddy
donna
edna
charlie
betty
FALSE
Jess>
(state hall wet) (state kitchen dry) (is-raining false) (location leak kitchen)Define a query which will return the location of the leak.