Unlike a fact, which is static or unchanging variables are dynamic. Variable identifier is always written by a question mark followed by the name of the variable, for example ?my-var.
CLIPS> (defrule make-duck-sound (duck-sound ?sound) => (printout t "Duck makes " ?sound crlf)) CLIPS> (assert (duck-sound quack)) <Fact-0> CLIPS> (run) Duck makes quack
CLIPS gives an error message when it cannot find a value bound(assignment f value to a variable) to ?sound. Variables are user commonly in printing output. More than one variable may be used in a pattern:
CLIPS> (defrule shots (duckshoot ?hunter ?who) => (printout t ?hunter " shot " ?who crlf)) CLIPS> (assert (duckshoot John duck)) <Fact-1> CLIPS> (run) John shot duck
Notice that a big difference the order of fields makes in determining who shot who. You can also see, that rule will not fire when the fact does not match second pattern constraint:
assert (duckshoot duck Jerry)) <Fact-2> CLIPS> (run) duck shot Jerry CLIPS> (assert (duckshoot duck)) <Fact-3> CLIPS> (run) CLIPS>
No output.
the “?” is also called a single-field contstraint. A single-field wildcard stands for exactly one field. It is much easier to use the multi-field wildcard. This is a “$?” symbols and represents zero or more fields. It is very useful and important.
Try to write simple search engine for a library. The authors of the books does not matter, the informations about books consists only a title, such as (title C++ tutorial). Titles are stored as a facts. Now write a rule that return all books with “CLIPS” word in the title:
CLIPS> (load "books.clp") Defining deffacts: books Defining defrule: find-clips-books +j TRUE CLIPS> (reset) CLIPS> (run) Clips book is () CLIPS (manual) Clips book is (Introduction to) CLIPS () Clips book is () CLIPS (user guide) CLIPS> (facts) f-0 (initial-fact) f-1 (title CLIPS user guide) f-2 (title Introduction to CLIPS) f-3 (title C++ tutorial) f-4 (title Assembler) f-5 (title CLIPS manual) For a total of 6 facts.
Sample code: download!
Retraction is very useful in expert systems. Before a fact can be retracted, it must be specified to CLIPS. To retract fact from a rule, the fact-address first must be bound to a variable. There is a big difference binding a variable to the contents of a fact and binding a variable to the fact-address. The fact-address is specified using the “←” symbols.
Simple rule to retract a Donald Duck from fact-database:
CLIPS> (defrule test ?address <- (duck Donald) => (retract ?address) (printout t "retracting Donald Duck" crlf)) CLIPS> (assert (duck Jasper) (duck Donald) (duck Harold)) <Fact-2> CLIPS> (run) retracting Donald Duck CLIPS> (facts) f-0 (duck Jasper) f-2 (duck Harold) For a total of 2 facts.
Now try to update books.clp program to retract all books with “CLIPS” in title. Sample verbose:
CLIPS> (load "books1.clp") TRUE CLIPS> (reset) CLIPS> (facts) f-0 (initial-fact) f-1 (title CLIPS user guide) f-2 (title Introduction to CLIPS) f-3 (title C++ tutorial) f-4 (title Assembler) f-5 (title CLIPS manual) For a total of 6 facts. CLIPS> (run) Clips book is () CLIPS (manual) Clips book is (Introduction to) CLIPS () Clips book is () CLIPS (user guide) CLIPS> (facts) f-0 (initial-fact) f-3 (title C++ tutorial) f-4 (title Assembler) For a total of 3 facts.
The changes in the file are small. Added one bind a variable to the fact-address and one retract:
(deffacts books "titles of books" (title CLIPS user guide) (title Introduction to CLIPS) (title C++ tutorial) (title Assembler) (title CLIPS manual)) (defrule find-clips-books ?what <- (title $?begin CLIPS $?end) => (printout t "Clips book is " ?begin " CLIPS " ?end ", retracting!" crlf) (retract ?what))
Sample code: download!
There are three types of connective constraint. First is ~ constraint. It acts on the one value that immediately follows it and will not allow that value. As a simple example write a rule that print out “Don't walk!” if the light is not green:
(defrule dont-walk (light ~green) => (printout t "Don't walk!" crlf))
Now assert fact (light green) and (light red) and watch the output. By using ~ constraint this one rule does the work of many other rules that required specifying each light condition.
The second connective constraint is | constraint. It is used to allow any of a group of values to match. As an example write a rule that will warn You if the light is yellow or blinking yellow:
(defrule warn (light yellow|blinking-yellow) => (printout t "Be cautious" crlf))
The third of connectove constraint is the & constraint, It forces connected constraints to match in union. It is used with the other constraints. As an example update the sample with warning when the light is yellow or blinking-yellow. Identify that is the light by binding a variable to the color that is matched using the & and then print out the variable
(defrule warn (light ?color&yellow|blinking-yellow) => (printout t "Be cautious, the light is " ?color crlf))
& constraint can be used with ~. Lets write a rule that runs when the light is not yellow and not blinking-yellow:
(defrule warn (light ?color&~yellow&~blinking-yellow) => (printout t "The light is " ?color crlf))
Run the example:
CLIPS> (assert (light green)) <Fact-0> CLIPS> (agenda) 0 warn: f-0 For a total of 1 activation. CLIPS> (run) The light is green