CLIPS Home page

Matching and Manipulating Facts

CLIPS Variable's

Assertive Variables

Asserting Variables to Printed Output

The Happy Bachelor

It's Not Important

Multifield Wildcard

Ideal Bachelor

 

Matching and Manipulating Facts

The type of rules that you've seen so far illustrates simple matching of patterns to facts. In this tutorial, you'll learn very powerful ways to match and manipulate facts.

CLIPS Variable’s

Just as with other programming languages, CLIPS has variables to store values. Unlike a fact, which is static or unchanging, the contents of a variable are dynamic as the values assigned to it change. In contrast, once a fact is asserted, retracting and asserting a new fact with the changed fields can only modify its fields.

The name of a variable, or variable identifier, is always written by a question mark followed by a symbol that is the name of the variable. The general format is

? <variable-name>

Global variables, have a slightly different syntax. Just as in other programming languages, variable names should be meaningful for good style. Some examples of valid variable names follow.

?noun ?color ?x

?sensor ?valve ?pies-eaten

Before a variable can be used, it should be assigned a value. As an example of a case where a value is not assigned, try to enter the following and CLIPS will respond with the error message shown.

CLIPS> (clear)

CLIPS> (defrule test

(initial-fact) ; asserted by a (reset) command

=>

(printout t ?x crlf)

Undefined variable x referenced in RHS of defrule.

ERROR:

(defrule MAIN: :test

(initial-fact)

=>

(printout t ?x crlf)

CLIPS>

 

CLIPS gives an error message when it cannot find a value bound to ?x. The term bound means the assignment of a value to a variable. Only global variables are bound in all rules. All other variables are only bound within a rule. Before and after a rule fires, nonglobal variables are not bound and so CLIPS will give an error message if you try to query a nonbound variable.

Assertive Variables

One common use of variables is to match a value on the LHS and then assert this bound variable on the RHS. For example, enter

(defrule make-woof

(dog-sound ?sound)

=>

(assert (sound-is ?sound)))

Now assert (dog-sound woof), then (run) the program. Check the facts and you'll see that the rule has produced (sound-is woof) because the variable ?sound was bound to woof.

Of course, you also can use a variable more than once. For example, enter the following. Be sure to do a (reset) and assert (dog-sound woof) again.

(defrule make-woof

(dog-sound ?sound)

=>

(assert (sound-is ?sound ?sound)))

When the rule fires, it will produce (sound-is woof woof) since the variable ?sound is used twice.

 

Asserting Variables to Printed Output

 

Variables also are used commonly in printing output, as in

(defrule make-quack

(duck-sound ?sound)

=>

(printout t "The duck said " ?sound crlf))

 

Do a (reset), enter this rule, and assert the fact and then (run) to find out what the duck said. How would you modify the rule to put double quotes around quack in the output?

More than one variable may be used in a pattern, as the following example shows.

CLIPS> (clear)

CLIPS> (defrule whodunit

(duckshoot ?hunter ?who)

=>

(printout t ?hunter " shot " ?who crlf))

CLIPS> (reset)

CLIPS> (assert (duckshoot Brian duck))

<Fact-1>

CLIPS> (run)

Brian shot duck ; Duck dinner tonight!

CLIPS> (assert (duckshoot duck Brian)

<Fact-2>

CLIPS> (run)

duck shot Brian :Brian dinner tonight!

CLIPS> (assert (duckshoot duck) ) :Missing third field

<Fact-3>

CLIPS> (run)

CLIPS> : Rule doesn't fire, no output

 

Notice what a big difference the order of fields makes in determining who shot who. You can also see that the rule did not fire when the single-field fact (duck) was asserted. The rule was not activated because no field of the fact matched the second pattern constraint, ?who.

 

 

The Happy Bachelor

Retraction is very useful in expert systems and usually done on the RHS rather than at the top-level. Before a fact can be retracted, it must be specified to CLIPS. To retract a fact from a rule, the fact-address first must be bound to a variable on the LHS.

There is a big difference between binding a variable to the contents of a fact and binding a variable to the fact-address. In the examples that you've seen such as (dog-sound ?sound), a variable was bound to the value of a field. That is, ?sound was bound to woof. However, if you want to remove the fact whose contents are (dog-sound woof), you must first tell CLIPS the address of the fact to be retracted.

The fact-address is specified using the left arrow, "<-". To create this, just type a "<"symbol followed by a "-". As an example of fact retraction from a rule.

CLIPS> (clear)

CLIPS> (assert (bachelor Dopey))

<Fact-0>

CLIPS> (facts)

f-0 (bachelor Dopey)

For a total of 1 fact.

CLIPS> (defrule get-married

?duck <- (bachelor Dopey)

=>

(printout t "Dopey is now happily married" ?duck crlf)

(retract ?duck))

CLIPS> (run)

Dopey is now happily married<Fact-0>

CLIPS> (facts)

CLIPS>

 

Notice that the (printout) prints the fact-index of ?duck, <Fact-0>, since the left arrow bound the address of the fact to ?duck. Also, there is no fact (bachelor Dopey) because it has been retracted.

Variables can be used to pick up a fact value at the same time as an address, as shown in the following example. For convenience, a (deffacts) has also been defined.

CLIPS> (clear)

CLIPS> (defrule marriage

?duck <- (bachelor ?name)

=>

(printout t ?name is now happily married" crlf)

(retract ?duck)) I

CLIPS> (deffacts good-prospects

(bachelor Dopey)

(bachelor Dorky)

(bachelor Dicky))

CLIPS> (reset)

CLIPS> (run)

Dicky is now happily married

Dorky is now happily married

Dopey is now happily married

CLIPS>

 

Notice how the rule fired on all facts that matched the pattern (bachelor ?name).

 

It’s Not Important

 

Instead of binding a field value to a variable, the presence of a nonempty field can be detected alone using a wildcard. For example, suppose you're running a dating service for ducks, and a duckette asserts that she only dates ducks whose first name is Richard. Actually, two criteria are in this specification since there is an implication that the duck must have more than one name. So a plain (bachelor Richard) isn't adequate because there is only one name in the fact.

This type of situation, in which only part of the fact is specified, is very common and very important. To solve this problem, a wildcard can be used to fire the Richards.

The simplest form of wildcard is called a single-field wildcard and is shown by a question mark, "?". The "?" is also called a single-field constraint. A single-field wildcard stands for exactly one field, as shown following.

CLIPS> (clear)

CLIPS> (defrule dating-ducks

(bachelor Dopey ?)

=>

(printout t "Date Dopey" crlf)

CLIPS>

(deffacts duck

(bachelor Dicky)

(bachelor Dopey)

(bachelor Dopey Mallard)

(bachelor Dinky Dopey)

(bachelor Dopey Dinky Mallard))

CLIPS> (reset)

CLIPS> (run)

Date Dopey

CLIPS>

 

The pattern includes a wildcard to indicate that Dopey's last name is not important. So long as the first name is Dopey, the rule will be satisfied and fire. Because the pattern has three fields of which one is a single-field wildcard, only facts of exactly three fields can satisfy it. In other words, only Dopeys with exactly two names can satisfy this duckette.

Suppose you want to specify Dopeys with exactly three names? All that you'd have to do is write a pattern like

(bachelor Dopey ? ?)

 

or, if only persons with three names whose middle name was Dopey,

(bachelor ? Dopey ?)

 

or, if only the last name was Dopey, as in the following:

(bachelor ? ? Dopey)

 

Another interesting possibility occurs if Dopey must be the first name, but only those Dopeys with two or three names are acceptable. One way of solving this problem is to write two rules. For example

(defrule eligible

(bachelor Dopey ?)

=>

(printout t "Date Dopey" crlf))

(defrule eligible-three-names

(bachelor Dopey ? ?)

=>

(printout t "Date Dopey" crlf)

 

Enter and run this and you'll see that Dopeys with both two and three names are printed. Of course, if you don't want anonymous dates, you need to bind the Dopey names with a variable and print them out.

Multifield Wildcard

Rather than writing separate rules to handle each field, it's much easier to use the multifield wildcard. This is a dollar sign followed by a question mark, "$?" , and represents zero or more fields.

The two rules for dates can now be written in a single rule as follows.

CLIPS> (clear)

CLIPS> (defrule dating-ducks

(bachelor Dopey $?)

=>

(printout t "Date Dopey" crlf))

CLIPS> (deffacts duck

(bachelor Dicky)

(bachelor Dopey)

(bachelor Dopey Mallard)

(bachelor Dinky Dopey)

(bachelor Dopey Dinky Mallard))

CLIPS> (reset)

CLIPS> (run)

Date Dopey

Date Dopey

Date Dopey

CLIPS>

 

Wildcards have another important use because they can be attached to a symbolic field to create a variable such as ?x, $?x, ?name, or $?name. The variable can be a singlefleld variable or a multifield variable depending on whether a "?"or "$?" is used on the LHS. Note that on the RHS only a ?x is used, where the "x" can be any variable name. You can think of the "$" as a function whose argument is a single-field wildcard or a single-field variable and returns a multifield wildcard or a multifield variable, respectively.

As an example of a multifield variable, the following version of the rule also prints out the name field(s) of the matching fact because a variable is equated to the name field(s) that match:

CLIPS> (defrule dating-ducks

(bachelor Dopey $?name)

=>

(printout t "Date Dopey " ?name crlf))

CLIPS> (reset)

CLIPS> (run)

Date Dopey (Dinky Mallard)

Date Dopey (Mallard)

Date Dopey ( )

CLIPS>

 

As you can see, on the LHS, the multifield pattern is $?name but is ?name when used as a variable on the RHS. When you enter and run, you'll see the names of all eligible Dopeys. The multifield wildcard takes care of any number of fields. Also, notice that multifleld values are returned enclosed in parentheses.

Suppose you wanted a match of all ducks who had a Dopey somewhere in their name, not necessarily as their first name. The following version of the rule would match all facts with a Dopey in them and then print out the names:

CLIPS> (defrule dating-ducks

(bachelor $?first Dopey $?last)

(printout t "Date " ?first " Dopey " ?last crlf)

CLIPS> (reset)

CLIPS> (run)

Date ( ) Dopey (Dinky Mallard)

Date (Dinky) Dopey ( )

Date ( ) Dopey (Mallard)

Date ( ) Dopey ( )

CLIPS>

 

The pattern matches any names that have a Dopey anywhere in them.

Single- and multifield wildcards can be combined. For example, the pattern

(bachelor ? $? Dopey ?)

means that the first and last names can be anything and that the name just prior to the last must be Dopey. This pattern also requires that the matching fact will have at least four fields, since the "$?" matches zero or more fields and all the others must match exactly four.

The Ideal Bachelor

Variables used in patterns have an important and useful property, which can be stated as follows.

-The first time a variable is bound it retains that value only within the rule, both on the LHS and also on the RHS, unless changed on the RHS.

For example, in the rule below

(defrule bound

(number-1 ?num)

(number-2 ?num)

=>)

 

If there are some facts

f-0 (number-l 0)

f-l (number-2 0)

f-2 (number-l 1)

f-3 (number-2 1)

then the rule can only be activated by the pair f-0, f-1, and the other pair f-2, f-3. That is, fact f-0 cannot match with f-3 because when ?num is bound to 0 in the first pattern, the value of ?num in the second pattern also must be 0. Likewise, when ?num is bound to 1 in the first pattern, the value of ?num in the second pattern must be 1. Notice that the rule will be activated twice by these four facts: one activation for the pair f-0. f-1, and the other activation for the pair f-2, f-3.

As a more practical example, enter the following rule. Notice that the same variable, ?name, is used in both patterns.

CLIPS> (clear)

CLIPS> (defrule ideal-duck-bachelor

(bill big ?name)

(feet wide ?name)

=>

(printout t "The ideal duck is ?name crlf))

CLIPS> (deffacts duck-assets

(bill big Dopey)

(bill big Dorky)

(bill little Dicky)

(feet wide Dopey)

(feet narrow Dorky)

(feet narrow Dicky))

CLIPS> (reset)

CLIPS> (run)

The ideal duck is Dopey

CLIPS>

 

When the program is run, the first pattern matches Dopey and Dorky since they both have big bills. The variable ?name is bound to each name. when CLIPS tries to match the second pattern of the rule, only the variable ?name which is bound to Dopey also satisfies the second pattern of (feet wide).

 

CLIPS Home page