==== CLIPS variables and wildcards ==== === Variables === 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)) 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)) 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)) CLIPS> (run) duck shot Jerry CLIPS> (assert (duckshoot duck)) CLIPS> (run) CLIPS> No output. === Single-field and multi-field wildcards === 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: {{:clips:books.zip|download!}} === Fact-address === 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)) 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: {{:clips:books1.zip|download!}} === Field constraint === 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)) CLIPS> (agenda) 0 warn: f-0 For a total of 1 activation. CLIPS> (run) The light is green