==== 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