Académique Documents
Professionnel Documents
Culture Documents
Supplied as a single .zip file, used on all supported platform Contains all thats needed except for a JVM Unpack to a folder Jess70p2\ containing
README: Quick start guide LICENSE: Info about your rights bin\ : Contains a Windows batch file (jess.bat) and a UNIX shell script (jess) to start the Jess command prompt lib\: Contains
Jess itself, as a Java archive file. Not "clickable" the JSR-94 (javax.rules) API in the file jsr94.jar
docs\: index.html is the entry point for the Jess manual. examples\jess\: Small example programs. examples\xml\: Small example programs in JessML, Jess's XML rule language. eclipse\: JessDE, Jess's Integrated Development Environment, plugins for Eclipse 3.0
Documentation in Jess70p2/docs/eclipse.html
src\ : source for Jess rule engine and development environment, including an Ant script
Command-line Interface
To execute a file of Jess code from the Jess prompt, use the batch command:
Jess> (batch "../examples/jess/sticks.clp") Who moves first (Computer: c Human: h)?
Pass the name of the program as an argument to the Jess script Below, for variety, we go down only to the Jess70p2 folder
C:\Program Files\Jess70p2>bin\jess examples\jess\sticks.clp Jess, the Rule Engine for the Java Platform Copyright (C) 2006 Sandia Corporation Jess Version 7.0p1 12/21/2006 Who moves first (Computer: c Human: h)?
Treated as whitespace
3.1. Symbols
Can contain letters, numbers, and any of $*=+/<>_?# Case sensitive May not begin with a number May begin with some punctuation marks
Dashes are traditional word separators Example valid symbols: foo first-value contestant#1 _abc
3.2. Numbers
Jess uses Java functions to parse numbers The following are all valid numbers: 3 4. 5.643 5654L 6.0E4 1D
3.3. Strings
Character strings are denoted using " \ used to escape embedded quote symbols No escape sequences recognized
But real newlines are allowed inside a double-quoted string and become part of it Some valid strings:
"foo" "Hello, World" "\"Nonsense,\" he said firmly." "Hello, There"
3.4. Lists
E.g.:
(+ 3 2) (a b c) ("Hello, World") () (deftemplate foo (slot bar))
3.5. Comments
C-style block comments: /* */ Lisp-style line comments begin with a ; and go to the end of the linee.g.,. ; This is a list (a b c)
No operators
Can define your own functions in the Jess language and in Java But many built-in functions
3.7. Variables
A variable can refer to a single symbol, number, or string, or to a list Assign a value to a variable using function binde.g., Jess> (bind ?x 12) 12
Variables arent declared except for defglobals To see the value of a variable, type it at the prompte.g., Jess> ?x 12
Variables created
To create global variables not destroyed by reset, use the defglobal construct (defglobal [?<global-name> = <value>]+)
Global variable names begin (after the ?) and end with a *e.g., ?*a* ?*all-values* ?*counter* When reset is issued, global variable may be reset to their initial values
Depends on the current setting of the reset-globals property Function set-reset-globals sets this property
Jess> (defglobal ?*x* = 3 ?*y* = 6) TRUE Jess> (bind ?*x* 4) 4 Jess> (reset) TRUE Jess> ?*x* 3 Jess> (set-reset-globals nil) FALSE Jess> (bind ?*x* 4) 4 Jess> (reset) TRUE Jess> ?*x* 4
Control flow, like everything in Jess, is done by function calls Thus functions if, while, for, try
Iteration
(while <expression> [do] <action>*)
Evaluates <expression> If true, evaluates all <action> arguments Repeats until <expression> evaluates to FALSE, then returns FALSE File while.clp:
(bind ?i 3) (while (> ?i 0) (printout t ?i crlf) (- ?i) ) Jess> (batch "../myExamples/while.clp") 3 2 1 FALSE
Like the Java construct Loop continues until the condition is false or return is encountered
File for.clp:
(bind ?j 0) (for (bind ?i 3) (> ?i 0) (-- ?i) (printout t ?i crlf) (++ ?j) (printout t " " ?j crlf) ) Jess> (batch "../myExamples/for.clp") 3 1 2 2 1 3 FALSE
Set <variable> to each of the list elements in turn Exit when the list is exhausted or return is encountered File foreach.clp:
(foreach ?x (create$ a b c d) (printout t ?x crlf) ) Jess> (batch "../myExamples/foreach.clp") a b c d
the 1st list of actions is evaluated and the return value is whats returned by the last action of the list
the 2nd list of actions is evaluated and the value of the last is returned
File simple_if.clp:
(printout t "Enter an integer" crlf) (bind ?x (read t)) (if (> ?x 100) then (printout t "A big number" crlf) (* ?x ?x) ) Jess> (batch "../myExamples/simple_if.clp") Enter an integer 101 A big number 10201
File if_else.clp:
(printout t "Enter an integer" crlf) (bind ?x (read t)) (if (> ?x 100) then (printout t "A big number" crlf) (* ?x ?x) else (printout t "A little number" crlf) (sqrt ?x) ) Jess> (batch "../myExamples/if_else.clp") Enter an integer 49 A little number 7.0
<function-name> must be a symbol Each <parameter> must be a variable name <doc-comment> is a double-quoted string describing the function Any number of <expr> expressions <return-specifier> gives the return value of the function
Either an explicit return or any value or expression last encountered in executing the function
(deffunction max "Return the larger of 2 numerical arguments" (?a ?b) (if (> ?a ?b) then (return ?a) else (return ?b)) ) Jess> (batch "myExamples/max.clp") TRUE Jess> (max 3 5) 5
For a deffunction taking any number of arguments, make the last formal parameter be a multifield: a variable with '$' before the ?
When the deffunction is called, the multifield variable contains a list all remaining arguments passed No more than one such wildcard argument The last argument to the function
(deffunction max ($?args) (bind ?mx 0) (foreach ?x $?args (if (> ?x ?mx) then (bind ?mx ?x) ) ) (return ?mx) ) Jess> (batch "myExamples/max2.clp") TRUE Jess> (max 4 2 5 3) 5
4.2. defadvice
defadvice lets you wrap extra code around a Jess function so that
to alter the argument list seen by the real function, or short-circuit it completely by returning a value of its own to see the return value of the real function and possibly alter it
Lets add-on authors extend Jess without changing internal code E.g., intercept calls to + and add an extra argument 1
$?argv is a magic variable containing a list of the function name and the arguments of the call
Jess> (defadvice before + (bind $?argv (create$ $?argv 1))) TRUE Jess> (+ 1 2) 4
E.g., keep the real function from being called, make all additions equal to 1
Jess> (defadvice before + (return 1)) TRUE Jess> (+ 1 2) 1
?retval is a magic variable; its value is the real functions return value
List Functions
create$ or list
length$
first$
Takes a list argument, returns a list containing only the 1st element of it
Jess> (first$ ?list1) (1)
rest$
Takes a list argument and returns a list of all its arguments but the 1 st
Jess> (rest$ ?list1) (2 3)
nth$
Takes and integer index and a list, returns the element at that index
listp
union$
Passed 1 or more lists, returns their union (concatenation with duplicates removed)
Jess> (bind ?list2 (list 2 4 6)) (2 4 6) Jess> (bind ?list3 (list 1 3 5)) (1 3 5) Jess> (union$ ?list1 ?list2 ?list3) (1 2 3 4 6 5)
intersection$
Returns the index of the value of the expression in the list or FALSE if this value is not in the list.
Jess> (member$ 4 ?list2) 2 Jess> (member$ (+ 3 1) ?list2) 2 Jess> (member$ 5 ?list2) FALSE
Returns a new list like the original but with 1 or more values inserted starting at the index given
To append an element,
Jess> (insert$ ?list1 (+ 1 (length$ ?list1)) 9) (1 2 3 9)
Returns a new list like the original but with the elements from the beginning to the end index deleted
Jess> (bind ?list4 (list 2 4 6 8 10 12)) (2 4 6 8 10 12) Jess> (delete$ ?list4 2 4) (2 10 12) Jess> (delete$ ?list4 2 2) (2 6 8 10 12) Jess> (delete$ ?list1 1 1) (2 3) Jess> (delete$ ?list1 2 (length$ ?list1)) (1)
the elements from the beginning to the end index replaced by the elements in the one or more lists at the end
Jess> (replace$ ?list4 2 4 (list 0 1 0)) (2 0 1 0 10 12) Jess> (replace$ ?list4 2 4 (list 0 1 0) (list 1 0 1)) (2 0 1 0 1 0 1 10 12) Jess> (replace$ ?list4 2 4 (list)) (2 10 12)
relations)
Returns TRUE if the 1st argument is equal in type and value to all subsequent arguments.
But returns TRUE if the 1st argument is merely equivalent to all the otherssupports type conversion
Jess> (bind ?lis ?list1) (1 2 3) Jess> (eq ?lis ?list1) TRUE Jess> (eq* ?list1 (list 1 2 3)) TRUE Jess> (= 2 2.0) TRUE Jess> (eq* 2 2.0) TRUE Jess> (eq 2 2.0) FALSE
Arithmetic Operators
+
Passed 1 or more numbers, subtracts from the first all the rest
mod
**
Takes 2 numbers, returns a FLOAT thats the 1st raised to the 2nd Passed 1 or more numbers, returns the smallest
min
max
Auto-increment and (++ <variable>) decrement Increments the value of the variable (must be numerical), returns the
Decrements the value of the variable (must be numerical), returns the result
Boolean Functions
not
and
or
Functionals
apply
Returns result of calling the 1st argument, as a Jess function, on all the remaining arguments
1st argument may be a variable whose value is the name (a string) of a Jess or user-defined function
Jess> (bind ?op "+") "+" Jess> (apply ?op 5 2 1) 8 Jess> (bind ?op "-") "-" Jess> (apply ?op 5 2 1) 2 Jess> (deffunction inc2 (?x) (+ 2 ?x)) TRUE Jess> (bind ?f_name "inc2") "inc2" Jess> (apply ?f_name 3) 5
Jess> (map "inc2" (list 1 2 3)) (3 4 5) Jess> (map ?f_name (list 1 2 3)) (3 4 5) Jess> (map "abs" (list -1 2 -3)) (1 2 3)
Such an anonymous functions is useful if used only once, as with map Jess> (map (lambda (?x) (+ 2 ?x)) ?list1) (3 4 5)
Jess> (bind ?x "(length$ (list 1 2 3 4))") "(length$ (list 1 2 3 4))" Jess> (eval ?x) 4 build is a synonym for eval
For historical reasons, build is generally used with rules, eval with function calls This functionality lets Jess create and incorporate new rules as it runs
Facts in Jess Manipulating the Working Memory The watch and facts Functions
The working memory (or fact base) is a collection of facts (in the technical sense) watch tells Jess to print messages when various interesting things happen
Different arguments get Jess to report on different kinds of events (watch facts) gets Jess to report when facts are added or removed
Function reset initializes the working memory and creates fact (MAIN::initial-fact)
Jess signals
Lets you easily refer to the fact for changing or removing it Jess uses fact-ids when deciding the order for firing rules
Returns the fact-id of the last asserted fact Or FALSE if the last fact couldnt be asserted
Jess> (retract 1) TRUE Jess> (facts) f-0 (MAIN::initial-fact) For a total of 1 facts in module MAIN. Jess> (bind ?f (fact-id 0)) <Fact-0> Jess> (retract ?f) TRUE Jess> (facts) For a total of 0 facts in module MAIN.
Using a reference (if you already have it) is faster for Jess
clear removes from working memory all facts and variables, rules, and deffunctions
To restore the initial state without erasing entire application, use reset
Puts working memory into a known state Includes at least initial fact (MAIN::initial-fact) Jess uses this fact internally Many rules dont work without it
A deffacts is a list of facts asserted into working memory when reset is issued
Jess> (clear) TRUE Jess> (deffacts catalog "Product catalog" (product 354 sticky-notes "$1.99") (product 355 paper-clips "$0.99") (product 356 blue-pens "$2.99")) TRUE Jess> (facts) For a total of 0 facts in module MAIN.
Continued
Jess> (reset) TRUE Jess> f-0 f-1 f-2 f-3 For a (facts) (MAIN::initial-fact) (MAIN::product 354 sticky-notes "$1.99") (MAIN::product 355 paper-clips "$0.99") (MAIN::product 356 blue-pens "$2.99") total of 4 facts in module MAIN.
Kinds of Facts
3 kinds of facts An unordered fact has named data fields like a DB tables columns
(person (name John Doe) (age 34) (height 5 11) (weight 225))
Unordered Facts
The deftemplate Construct
Before asserting an unordered fact, use a deftemplate to define the slots for the kind of fact
Jess> (deftemplate person "People in actuarial database" (slot name) (slot age) (slot gender)) TRUE Jess> (assert (person (age 34) (name "Bill Jones") (gender Male))) <Fact-1>
Continued
Jess> (facts) f-0 (MAIN::initial-fact) f-1 (MAIN::person (name "Bill Jones") (age 34) (gender Male)) For a total of 2 facts in module MAIN.
The name of the deftemplate (here person) provides the head of the facts Can omit slots in asserting an unordered fact
Jess> (assert (person (age 30) (gender Female))) <Fact-2> Jess> (facts) f-0 (MAIN::initial-fact) f-1 (MAIN::person (name "Bill Jones") (age 34) (gender
Male))
f-2 (MAIN::person (name nil) (age 30) (gender Female)) For a total of 3 facts in module MAIN.
If nil isnt an acceptable default value, specify one with a slot qualifier
Jess> (clear) TRUE Jess> (deftemplate person "People in actuarial database" (slot name (default OCCUPANT)) (slot age) (slot gender)) TRUE Jess> (assert (person (age 30) (gender Female))) <Fact-0> Jess> (facts) f-0 (MAIN::person (name OCCUPANT) (age 30) (gender Female)) For a total of 1 facts in module MAIN.
Multislots
Often a rule acts on a fact to change slot values modify takes as its first argument a Fact object or a numeric fact-id
All other arguments are slot/multislot name, value pairs It modifies the slots/multislots of the fact as per the given values
Jess> (modify 0 (age 23)) <Fact-0> Jess> (facts) f-0 (MAIN::person (name "Jane Doe") (age 23) (gender Female) (hobbies skiing "collecting antiques")) For a total of 1 facts in module MAIN.
duplicate is like modify but creates a new fact like the old but modified as specified
Returns the fact-id of the new fact or FALSE if no duplicate fact created
Jess> (duplicate 0 (name "John Doe") (gender Male)) <Fact-1> Jess> (facts) f-0 (MAIN::person (name "Jane Doe") (age 23) (gender Female) (hobbies skiing collecting antiques")) f-1 (MAIN::person (name "John Doe") (age 23) (gender Male) (hobbies skiing "collecting antiques")) For a total of 2 facts in module MAIN.
Can assert ordered facts as long as no deftemplate using the same head has been defined
Jess> (clear) TRUE Jess> (assert (number 123)) <Fact-0>
Ordered Facts
When you assert the 1st ordered fact with a given head, Jess generates an implied deftemplate for it ppdeftemplate takes a fact head, returns the implied template as a string, embedded quotes escaped
Jess> (ppdeftemplate number) "(deftemplate MAIN::number \"(Implied)\" (multislot __data))"
Note the 3 special templates used internally by Jess: __clear, __fact, __not_or_test_CE
The knowledge base is the collection of rules making up a rulebased system Rules take actions based on the contents of working memory 2 main classes of rules: forward-chaining and backwardchaining Access working memory directly with queries
Syntax similar to that of rules Search working memory, find specific facts, explore their relationships
Forward-chaining Rules
A rules then part can be executed whenever the if part is satisfied Define a rule with the defrule construct Simplest possible rule:
Jess> (defrule null-rule "A rule that does nothing" => ) TRUE
If you define another rule named null-rule, original is deleted Also an undefrule to delete a rule by name
Symbol => separates the rules LHS (if part) from its RHS (then part)
(watch activations) gets Jess to print a message when an activation record is placed on or removed from the agenda
An activation record associates a set of facts with a rule When the facts match the rules LHS, the rule should be executed
run tells Jess to start firing rules, returns number of rules fired
Rule engine fires the rules on the agenda, one at a time, until the agendas empty
Jess> (watch facts) TRUE Jess> (watch activations) TRUE Jess> (watch rules) TRUE Jess> (reset) ==> f-0 (MAIN::initial-fact) ==> Activation: MAIN::null-rule : TRUE Jess> (run) FIRE 1 MAIN::null-rule f-0 1
f-0
Since null-rule hasnt a LHS, Jess makes it conditional on the presence of the initial fact
LHS is a pattern (to match a fact in working memory), RHS is function calls No function call on LHS
Jess tries to find a fact (eq 1 1) in working memory To fire a rule based on evaluation of a function, use the test conditional element (later)
(watch all) gets Jess to print info on everything important that happens while the program runs
Jess> (clear) TRUE Jess> (watch all) TRUE Jess> (reset) ==> Focus MAIN ==> f-0 (MAIN::initial-fact) TRUE Jess> (deffunction change-baby () (printout t "Baby is now dry" crlf)) TRUE
Continued
Jess> (defrule change-baby-if-wet "If baby is wet, change its diaper" ?wet <- (baby-is-wet) Store a reference to fact (baby-is-wet) => in the pattern binding ?wet (change-baby) How Jess interprets (retract ?wet)) the rule internally MAIN::change-baby-if-wet: +1+1+1+t TRUE All LHS conditions of the rule are Jess> (assert (baby-is-wet)) ==> f-1 (MAIN::baby-is-wet) ==> Activation: MAIN::change-baby-if-wet : <Fact-1> Jess> (run) FIRE 1 MAIN::change-baby-if-wet f-1 Baby is now dry <== f-1 (MAIN::baby-is-wet) <== Focus MAIN 1
met by this list of factsjust 1 here
f-1
Rules not only react to the contents of working memory but also change it
One run can put info into working memory causing another to fire
Jess activates a run only once for a given working memory state Dont change baby again until a new baby-is-wet fact is asserted
Most patterns specify some set of slot values for the facts they match
Kinds of constraints
Literal constraint: Exact slot value Variable constraint: Bind a matched value to a variable Connective constraint: Combine conditions to match A and B or A or B Predicate constraint: Call a function to test a match Return value constraint: Test for an exact match between a slots contents and the result of a function call
Literal Constraints
A pattern including a literal value matches only facts that include that value
Jess> (clear) TRUE Jess> (defrule literal-values (letters b c) =>) TRUE Jess> (watch activations) TRUE Jess> (assert (letters b d)) <Fact-0> Jess> (assert (letters b c)) ==> Activation: MAIN::literal-values : <Fact-1> f-1
Everything that applies to ordered facts applies to the multislots of unordered facts
Likewise for the regular slots of unordered facts (but they hold only 1 value)
Variables as Constraints
Can use variables in place of literals for any part of the slot data A variable matches any value in that position in the facts matching the pattern
E.g., the following is activated each time an ordered fact with head a and 2 fields is asserted
Jess> (defrule simple-variables (a ?x ?y) => (printout t "'Saw 'a " ?x " " ?y "'" crlf))
Variables matched on the LHS of a rule are input for its RHS You can mix literal values and variables in the same pattern The same variable may occur in more than 1 pattern and more than once in a given pattern
Jess> (defrule repeated-variables (a ?x) (b ?x) => (printout t "?x is " ?x crlf)) TRUE Jess> (watch activations) TRUE Jess> (deffacts repeated-variable-facts (a 1) (a 2) (b 2) (b 3)) TRUE Jess> (reset) ==> Activation: MAIN::repeated-variables : TRUE Jess> (run) ?x is 2 1 f-2, f-3
Multifields
Can be used alone Used with single values, a multifield expands to match all thats not matched by other values
E.g., the pattern in the following matches any shopping-cart fact with a contents slot containing milk
(defrule cart-containing-milk (shopping-cart (contents $?before milk $?after)) => (printout t The cart contains milk. crlf))
A multifield contains the matched values as a (possibly empty) list On the RHS, can (and should, for style) omit the $ since there the multifield acts as a normal variable
Blank Variables
Used to specify that a multifield contains a certain arrangement of values E.g., (poker-hand ten ? ? ? ace)
In, e.g., (score ?*x*), the match considers the value of the defglobal when the fact is first asserted Subsequent changes to the defglobals value dont invalidate the match
Connective Constraints
Examples
Match any client fact with a city slot not containing Bangor
(client (city ~Bangor))
Match any client not from Bangor but remember the city in ?c
(client (city ?x&~Bangor))
If you cant express what you want with connective constraints, use predicate constraints
Use any predicate function as a constraint by preceding it with a : To use a slot value as a function argument,
bind the value to a variable then connect that binding to the function using &
Jess> (defrule small-order (shopping-cart (customer-id ?id) (contents $?c&:(< (length$ $?c) 5))) (checking-out-now ?id) => (printout t Wouldnt you like to buy more? crlf))
The slot data then must match what the function returns
E.g., find a pair of items s.t. the price of the 1st is 2 that of the 2nd
(item (price ?x)) (item (price =(* ?x 2)))
This is equivalent to
(item (price ?x)) (item (price ?y&:(eq ?y (* ?x 2))))
Pattern Bindings
To use retract, modify, or duplicate on a fact matched by a rules LHS, pass a handle to the fact to its RHS
Jess> (defrule pattern-binding ?fact <- (a "retract me") => (retract ?fact))
A reference to the jess.Fact object activating this rule is bound to ?fact when the rule is fired
To retrieve the facts name, its integer ID, and other data, call the Java member functions of the jess.Fact class directly
Jess> (defrule call-fact-methods ?fact <- (initial-fact) => (printout t "Name is " (call ?fact getName) crlf) (printout t "Id is " (call ?fact getFactId) crlf)) ==> Activation: MAIN::call-fact-methods : f-0 TRUE Jess> (reset) ==> Activation: MAIN::call-fact-methods : TRUE Jess> (run) Name is MAIN::initial-fact Id is 0 1 f-0
Pattern bindings must refer to specific facts Need care when using them with the grouping conditional elements in the following sections
Cant use them with not or test conditional elements When using them with or and and conditional elements, make sure the binding applies to only 1 fact
But, e.g., the and predicate function works on Boolean expressions while the and CE works on patterns The context always distinguishes
and matches multiple facts or matches alternative facts not matches if no facts match exists matches if at least 1 fact matches test matches if a function call doesnt evaluate to FALSE logical lets matching facts offer logical support to new facts
Continued
Jess> (assert (new-car (price 18000))) <Fact-0> Jess> (assert (used-car (mileage 30000))) <Fact-1> Jess> (run) 2 Jess> f-0 f-1 f-2 f-3 For a (facts) (MAIN::new-car (price 18000) (warrantyPeriod nil)) (MAIN::used-car (price nil) (mileage 30000)) (MAIN::candidate <Fact-1>) (MAIN::candidate <Fact-0>) total of 4 facts in module MAIN.
The rule can be activated as many times as there are facts to match
If the rules RHS tried to modify the mileage slot of the used-car template, runtime errors would occur whenever ?candidate is bound to a new-car fact
If a rules RHS uses a variable defined by matching on the LHS and the variable is defined by some but not all branches of an or pattern, then a runtime error may occur
Jess rearranges the pattern so that theres a single or at the top levele.g.,
Jess> (defrule prepare-sandwich (and (or (mustard) (mayo)) (bread)) =>) TRUE Jess> (ppdefrule prepare-sandwich) "(defrule MAIN::prepare-sandwich (or (and (mustard) (bread)) (and (mayo) (bread))) =>)"
A rule containing an or CE with n branches is equivalent to n rules, each with 1 of the branches on its LHS
E.g., if the original rule is removed, all associated subrules are removed
Then the pattern matches if a fact (or set of facts) the enclosed patter is not found
E.g., the following fires if there are no cars at all or if there are only cars of colors other than red
Jess> (defrule no-red-cars (not (auto (color red))) => )
Because a not pattern matches the absence of a fact, it cant define any variables used one the RHS or in subsequent patterns on the LHS
But variables can be introduced in a not pattern as long as theyre used in that patterne.g.,
Jess> (defrule no-odd-number (not (number ?n&:(oddp ?n))) => (printout t There are no odd numbers. crlf))
And a not pattern cant have a pattern binding: it doesnt match an actual fact
The matching happens during the assert, definstance, modify, duplicate, or reset function call creating the fact
When a fact matching what it encloses is asserted (the pattern match fails) When a fact matching what it encloses is removed ( the pattern match succeeds) When the pattern immediately before the non on the rules LHS is evaluated
If a not CE is
the 1st pattern on a rules LHS, the 1st pattern in an and group, or the 1st pattern on a given branch of an or group, then the pattern (initial-fact) is inserted before the not to become the preceding pattern in question
So its important to issue (reset) before running the rule engine The not CE can be used in arbitrary combination with the and and or CEs
E.g., the following fires once and only once if, for every car of a given color, theres a bus of the same color
Jess> (defrule forall-example (not (and (car (color ?c)) (not (bus (color ?c))))) =>)
The exists CE is shorthand for 2 nots nested one inside the other An exists CE is true if there exist any facts matching the enclosed pattern Its useful when you want a rule to fire only once even though there may be many facts that cold activate it
Jess> (defrule exists-an-honest-man (exists (honest ?)) => (printout t There is at least 1 honest man. crlf))
Cant bind any variables in an exists CE for later in the rule Cant use pattern bindings with exists
The body of a test pattern isnt a pattern to match against working memory but a Boolean function
Jess> (deftemplate person (slot age)) TRUE Jess> (defrule find-trusworthy-people-1 (person (age ?x)) (test (< ?x 30)) => (printout t ?x is under 30. crlf))
A test CE cant contain any variables not bound before it It cant have a pattern binding
A test CE is evaluated every time the preceding rule on the LHS is evaluated (like not)
Jess> (defrule find-trustworth-people-2 (person (age ?x&:(< ?x 30))) => (printout t ?x is under 30. crlf))
Jess inserts the pattern (initial-fact) as a preceding pattern for the test when a test CE is
the 1st pattern on the LHS, the 1st pattern in an and CE, or the 1st pattern in the branch of an or CE
Water flowing from a faucet has a logical dependency on the faucet being open
Jess> (defrule turn-water-on (faucet open) => (assert (water flowing)) TRUE Jess> (defrule turn-water-off (not (faucet open)) ?water <- (water flowing) => (retract ?water)) TRUE
All facts asserted on the RHS logically depend on any facts matching a pattern inside a logical CE on the LHS If any of the matches later become invalid, the dependent facts are retracted
Jess> (clear) TRUE Jess> (defrule water-flows-while-faucet-is-open (logical (faucet open)) => (assert (water flowing))) TRUE Jess> (assert (faucet open)) <Fact-0> Jess> (run) 1 Jess> f-0 f-1 For a (facts) (MAIN::faucet open) (MAIN::water flowing) total of 2 facts in module MAIN.
Continued
Jess> (watch facts) TRUE Jess> (retract 0) <== f-0 (MAIN::faucet open) <== f-1 (MAIN::water flowing) TRUE Jess> (facts) For a total of 0 facts in module MAIN.
If fact 1 logically depends on fact 2, fact 1 receives logical support from fact 2
If an unconditionally supported fact also receives explicit logical support, removing that support doesnt cause the fact to be retracted
Jess> (call (nth$ 1 (dependents 0)) getName) "MAIN::water" Jess> (call (call (nth$ 1 (dependencies 1)) fact 1) getName) "MAIN::faucet"
Backward-chaining Rules
So far weve seen forward-chaining rules In backward-chaining (goal seeking), if the LHS is only partially matched and the engine determines that firing some other rule would cause it to be fully matched, then the engine tries to fire the 2nd rule
Jesss backward chaining isnt transparent to the programmer and is simulated with forward-chaining rules
Use backward chaining to avoid computing the factorial of a number more than once The deftemplate factorial stores computed factorialse.g., (factorial 5 125) Rule print-factorial-10 has in its LHS (factorial 10 ?r1) Register factorial for backward chaining so this pattern causes Jess to assert (need-factorial 10 nil) Theres a do-factorial rule with LHS (need-factorial ?x ?) to compute the factorial of ?x and assert the result as a factorial fact
Patterns that match backward-chaining reactive deftemplates are goals If, after (reset), nothing matches the goal, a fact is inserted into working memory like (need-factorial 10 nil)
The facts head is constructed by adding need- to the goals head need-x facts are goal-seeking or trigger facts
Write a rule matching need-factorial trigger facts to compute and assert factorial facts
Jess> (defrule do-factorial (need-factorial ?x ?) => (bind ?r 1) (bind ?n ?x) (while (> ?n 1) (bind ?r (* ?r ?n)) (bind ?n (- ?n 1))) (assert (factorial ?x ?r))) TRUE
The rule compiler adds a negated match for the factorial pattern to the LHS
Jess> (reset) TRUE Jess> (watch all) TRUE Jess> (run) FIRE 1 MAIN::do-factorial f-1, ==> f-2 (MAIN::factorial 10 3628800) ==> Activation: MAIN::print-factorial-10 : FIRE 2 MAIN::print-factorial-10 f-0, f-2 The factorial of 10 is 3628800 <== Focus MAIN
f-0, f-2
Conflict Resolution
The set of activated rules eligible to be fired is the conflict set Putting the rules in firing order is conflict resolution The output of conflict resolution is the ordered list of activations, the agenda
Jesss conflict resolution is controlled by pluggable conflictresolution strategies Jess comes with 2:
depth (default): fire most recently activated rule first breadth: fire in activation order
(set-strategy breadth)
Function set-strategy changes the strategye.g., The strategy often makes no difference, but sometimes it does The depth strategy is intuitive and correct in most cases
Then call set-strategy with the name of your class as the argument
Compare 2 activations Return -1, 1, or 0 indicating 1st, 2nd, or either should fire 1st
Use rule salience to tell the conflict resolver to treat special rules (e.g., ones reporting security breaches) specially Each rule has a salience property giving its priority
Default salience is 0
Specify salience using literal integers, global variables, or function calls Current salience evaluation method determines how salience values are evaluated: 3 possible values:
when-defined (default): fixed salience value computed when the rules defined when-activated: salience reevaluated each time the rule is activated every-cycle: salience of every rule on the agenda recomputed after every rule firing (computational expensive)
Negative impact on performance Bad style in rule-based programming to force an order on rule firings
If youre using more than 2 or 3 salience values, consider implementing your algorithm with deffunctions or Java
Examples
To find out what a given API method does with a given argument, its faster to start Jess and type one line of Jess code than to write, compile and run a small Java program
Or, to experiment with arrangements of a GUI, create the graphical components with a few lines of Jess code then interactively assemble and arrange them
Can create Integer and String objects without explicitly importing that package
HashMap has a constructor that takes a Java int and a Java float as arguments
When you call a Java method, Jess converts the arguments from Jess data types to Java types as per the following table
Possible Java types The wrapped object a null reference String, java.lang.Boolean, or boolean String, char, or java.lang.Character float, double, and their wrappers long, short, int, byte, char, and their wrappers long, short, int, byte, char, and their wrappers A Java array
Jess type RU.Java-Object The symbol nil The symbol TRUE or FALSE RU.ATOM (a symbol), RU.STING RU.FLOAT RU.INTEGER RU.LONG RU.LIST
If an argument is passed to a Java constructor or method, Jess has the java.lang.Class object representing the formal parameters type a jess.Value object containing the value passed
E.g., symbol TRUE can be passed to a function expecting a boolean argument or to one expecting a String argument
Given a reference to a Java object in a Jess variable, you can invoke any of the objects methods using the call function
The 1st argument to call is a Java object The 2nd argument is the name of the invoked method The remaining arguments are the arguments passed to the method
The arguments are converted as per the above table E.g., use HashMap.put to associate some keys with values in our example, and HashMap.get to look up a value by key:
Jess> (call ?prices put bread 0.99) Jess> (call ?prices put peas 1.99) Jess> (call ?prices put beans 1.79) Jess> (call ?prices get peas) 1.99
Values returned by Java methods are converted to Jess types as per the following table
Jess type The symbol nil The symbol nil RU.STRING The symbol TRUE or FALSE RU.INTEGER RU.LONG RU.FLOAT RU.ATOM (a symbol) A list RU. Java-Object
Java type A null reference A void return value String boolean or java.lang.Boolean byte, short, int, or their wrappers long or java.lang.Long double, float, or their wrappers char or java.lang.Character An array Anything else
When the 1st element of a function call is a Java object, Jess assumes an implicit initial call
This works even if the 1st element of a function call is another function call
In both Java and Jess, can use the name of the Java class to invoke its static methodse.g.,
Jess> (call Thread sleep 1000)
Facts connecting working memory with the Java application in which Jess is running
One tool Jess includes for working with JavaBeans is a pair of methods to simplify accessing their data: (set Java-Object property value) (get Java-Object property)
For setters and getters, add set or get to the property name
Jess automatically converts Java arrays to plain lists (Values of type RU.LIST)cf. the 2 tables above E.g., call method keySet on our ?prices HashMap, then call method toArray on the result
To put the grocery list into a pop-up menu, pass the list as a constructor argument to the javax.swing.JComboBox class
Jess> (import javax.swing.JComboBox) TRUE Jess> (bind ?jcb (new JComboBox ?grocery-list)) <Java-Object:javax.swing.JComboBox>
Jess is much less picky about data types than is Java E.g., in Java, cant store a float into a HashMap
A java method name is overloaded if there are multiple methods with that name for that class with different parameter lists The Java compiler, faced with an overloaded methods name, chooses the most specific methods based on the parameter types But Jess hasnt the strict type info Java has
It chooses the 1st overload it finds matching the parameter types So many ways to convert between Jess and Java values
You want the boolean overload , but Jess calls the String one Create and pass a java.lang.Boolean object
Jess accesses public instance variables of Java objects using the get-member and set-member functions
Jess> (bind ?pt (new java.awt.Point)) <Java-Object:java.awt.Point> Jess> (set-member ?pt x 37) 37 Jess> (set-member ?pt y 42) 42 Jess> (get-member ?pt x) 37
Jess> (get-member System out) <Java-Object:java.io.PrintStream> Jess> ((get-member System out) println "Hi") Hi Jess> (get-member java.awt.BorderLayout NORTH) "North"
Jess converts values for all kinds of member variables as it does with method arguments and return values
When a Java method throws an exception (an object), Jess catches it and makes it available Jess also signals errors in your Jess code and in its own functions using exceptions When Jess catches an exception, its default action is to print a message, including 1 or 2 stack traces
If 1 trace, it shows where in Jesss own Java code the problem occurred If the exception occurs in a Java method called from Jess, a second trace locates the error in that method
In deployed code, whenever you call a method that might throw an exception, supply a handler to execute a response
The try function evaluates the expressions in its 1st block If one throws an exception, that block is abandoned
Jess> (deffunction parseInt (?String) (try (bind ?i (call Integer parseInt ?String)) (printout t "The answer is " ?i crlf) catch (printout t "Invalid argument" crlf))) TRUE Jess> (parseInt "10") The answer is 10 Jess> (parseInt "1O") Invalid argument
Its initialized by Jess to point to the caught exception Several methods can be called on it to get parts of the default message
Jess> (try (/ 2 "a") catch (bind ?ex ?ERROR)) <Java-Object:jess.JessException> Jess> (?ex toString) "Jess reported an error in routine Value.numericValue while executing (/ 2 \"a\"). Message: '\"a\"' is a string, not a number." Jess> (?ex getCause) Jess> (?ex getContext) " while executing (/ 2 \"a\")"
Continued
Jess> (?ex getData) "a number" Jess> (?ex getDetail) "'\"a\"' is a string, not " Jess> (?ex getLineNumber) -1
Function (instanceof Java-Object Class) returns TRUE if Java-Object can be assigned to a variable whose type is Class
Trivially, we have
Jess> (instanceof ?ex Exception) TRUE
But only one catch block in Jess But can use (instanceof Exception-Object Exception-Class) in multiple conditional branches
Works like throw in Java Its argument must be an extension of a Java class that extends java.lang.Throwable
Jess> (try (throw (new Exception "This went wrong")) catch (printout t (call ?ERROR getDetail) crlf)) Exception thrown from Jess language code