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))
<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.

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: 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))
<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!

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))
<Fact-0>
CLIPS> (agenda)
0      warn: f-0
For a total of 1 activation.
CLIPS> (run)
The light is green
© Antoni Ligeza 2009 Driven by DokuWiki