Original File

CLIPS User Guide Chapter 7
CLIPS User's Guide

Chapter 7 How to Be in Control

When you're young, you're controlled by the world, when you're older, you should control the world

Up to this point, you've been learning the basic syntax of CLIPS. Now you'll see how to apply the syntax you've learned to more powerful and complex programs. You'll also learn some new syntax for input, and see how to compare values and generate loops.

Let's Start Reading

Besides matching a pattern, a rule can get information in another way. CLIPS can read the information that you type from the keyboard using the read function.

The following example shows how (read) is used to input data. Note that no extra (crlf) is needed after the (read) to put the cursor on a new line. The (read) automatically resets the cursor to a new line.

CLIPS> (clear)

CLIPS> (defrule read-input

(initial-fact)

=>

(printout t "Name a primary color" crlf)

(assert (color (read))))

CLIPS>

(defrule check-input

?color <- (color ?color-read&red|yellow|blue)

=>

(retract ?color)

(printout t "Correct" crlf))

CLIPS> (reset)

CLIPS> (run)

Name a primary color

red

Correct

CLIPS> (reset)

CLIPS> (run)

Name a primary color

green

CLIPS> ; No "correct"

The rule is designed to use keyboard input on the RHS, so it's convenient to trigger the rule with (initial-fact). Otherwise, you'd have to make up some dummy fact to trigger the rule.

The (read) function is not a general-purpose function that will read anything you type on the keyboard. One limitation is that (read) will read only one field. So if you try to read

primary color is red

only the first field, "primary", will be read. To (read) all the input, you must enclose the input within double quotes. Of course, once the input is within double quotes, it is a single literal field. You can then access the substrings "primary", "color", "is", and "red" with the strexplode or sub-string functions.

The second limitation of (read) is that you can't input parentheses unless they are within double quotes. Just as you can't assert a fact containing parentheses, you can't (read) parentheses directly except as literals.

The readline function is used to read multiple values until terminated by a carriage return. This function reads in data as a string. In order to assert the (readline) data, an (assert- string) function is used to assert the nonstring fact, just as input by (readline). A top-level example of (assert-string) follows.

CLIPS> (clear)

CLIPS> (assert-string "(primary color is red)")

<Fact-0>

CLIPS> (facts)

f-0 (primary color is red)

For a total of 1 fact.

CLIPS>

Notice that the argument of (assert-string) must be a string The following shows how to assert a fact of multiple fields from (readline).

CLIPS> (clear)

CLIPS> (defrule test-readline

(initial-fact)

=>

(printout t "Enter input" crlf)

(bind ?string (readline))

(assert-string (str-cat "(" ?string ")")))

CLIPS> (reset)

CLIPS> (run)

Enter input

primary color is red

CLIPS> (facts)

f-0 (initial-fact)

f-1 (primary color is r ed)

For a total of 2 facts.

CLIPS>

Since (assert-string) requires parentheses around the string to be asserted, the (str- cat) function is used to put them around ?string.

Both (read) and (readline) also can be used to read information from a file by specifying the logical name of the file as the argument. For more information, see the CLIPS Reference Manual.

Being Efficient

CLIPS is a rule-based language that uses a very efficient pattern-matching algorithm called the Rete Algorithm, devised by Charles Forgy of Carnegie-Mellon University for his OPS shell. The term Rete is Latin for net, and describes the software architecture of the pattern-matching process.

It is very difficult to give precise rules that will always improve the efficiency of a program running under the Rete Algorithm. However, the following should be taken as general guidelines that may help:

1. Put the most specific patterns in a rule first. Patterns with unbound variables and wildcards should be lower down in the list of rule patterns. A control fact should be put first in the patterns.

2. Patterns with fewer matching facts should go first to minimize partial matches.

3. Patterns that are often retracted and asserted, volatile patterns, should be put last in the list of patterns.

As you can see, these guidelines are potentially contradictory. A non-specific pattern may have few matches (see guidelines 1 and 2). Where should it go? The overall guideline is to minimize changes of the partial matches from one cycle of the Inference Engine to the next. This may require much effort by the programmer in watching partial matches. An alternative solution is simply to buy a faster computer, or an accelerator board. This is becoming more attractive since the price of hardware always goes down while the price of human labor always goes up. Because CLIPS is designed for portability, any code developed on one machine should work on another.

Other Features

The test conditional element provides a very powerful way by which to compare numbers, variables, and strings on the LHS. The (test) is used as a pattern on the LHS. A rule will only be triggered if the (test) is satisfied together with other patterns.

Many predefined functions are provided by CLIPS as shown in the following table.

Predefined Functions

Logical Arithmetic

not Boolean not / division

and Boolean and * multiplication

or Boolean or + addition

- subtraction

Comparison

eq equal (any type). Compares type and magnitude

neq not equal (any type)

= equal (numeric type). Compares magnitude

<> not equal (numeric type)

>= greater than or equal to

> greater than

<= less than or equal to

< less than

All the comparison functions except "eq" and "neq" will give an error message if they are used to compare a number and non-number. If the type is not known in advance, the "eq" and "neq" functions should be used. The eq function checks for the same magnitude and type of its arguments while the "=" function only checks the magnitude of its (numeric) arguments and doesn't care if they're integer or floating-point.

The logical functions of CLIPS are and, or, and not. They can be used in expressions as Boolean functions. In CLIPS, true and false are represented by the symbols TRUE and FALSE. Note that upper-case must be used for logical values in CLIPS.

In addition to all the predefined functions, you may write external functions or user-defined functions in C, Ada, or other procedural languages and link to CLIPS. These external functions are then used as you would any predefined function.

CLIPS also gives you the capability of specifying an explicit and conditional element, an or conditional element, and a not conditional element on the LHS. Th e absence of a fact is specified as a pattern on the LHS using the "not" conditional element.

The alteration of our information to conform to reality is called truth maintenance. That is, we try to maintain the state of our minds to contain only true information so as to minimize conflicts with the real world.

While people can do this fairly easily (practice makes perfect), it's difficult for computers because they don't normally know which pattern entities are logically dependent on other pattern entities. CLIPS has a feature to support truth maintenance which will internally tag those pattern entities which are logically dependent on others. If these other pattern entities are retracted, CLIPS will automatically retract the logically dependent ones. The logical conditional element uses the keyword logical around a pattern to indicate that the matching pattern entities provide logical support to the assertions on the RHS.

Although the logical support works for assertions, it does not reassert retracted facts. The moral is, if you lose something due to erroneous information, you can't get it back (like losing money on your stockbrokers advice.)

CLIPS has two functions to help with logical support. The dependencies function lists the partial matches from which a pattern entity receives logical support, or none if there is no support. The second logic function is dependents which lists all pattern entities which receive logical support from a pattern entity.

The connective constraint, uses "&", "|", or "~". Another type of field constraint is called a predicate constraint and is often used for pattern matching of more complex fields. The purpose of a predicate constraint is to constrain a field depending on the result of a Boolean expression. If the Boolean returns FALSE, the constraint is not satisfied and the pattern matching fails. You'll find that the predicate constraint is very useful with numeric patterns.

A predicate function is one which returns a FALSE or a non-FALSE value. The colon "" followed by a predicate function is called a predicate constraint. The ":" may be preceded by "&", "|", or "~" or may stand by itself as in the pattern (fact :(> 2 1)). It is typically used with the & connective constraint as "&"

Predicate Function Check if <arg> is

(evenp <arg>) even number

(floatp <arg>) floating-point number

(integerp <arg>) integer

(lexemep <arg>) symbol or string

(numberp <arg>) float or integer

(oddp <arg>) odd number

(pointerp <arg>) external address

(sequencep <arg>) multifield value

(stringp <arg>) string

(symbolp <arg>) symbol

There are often cases in which it's convenient to have values which are globally known in an expert system. For example, it is inefficient to have to redefine universal constants such as [[pi]]. CLIPS provides the defglobal construct so that values may be universally known to all rules.

Another type of useful function is random numbers. CLIPS has a random function which returns a "random" integer value. The random number function of CLIPS actually returns pseudorandom numbers, which means they are not truly random but are generated by a mathematical formula. For most purposes the pseudorandom numbers will be fine. Note that the random function of CLIPS uses the ANSI C library function rand which may not be available on all computers that do not adhere to this standard. For more information on all these topics, please see the CLIPS Reference Manual.

In addition to control facts to control the execution of programs, CLIPS provides a more direct way of control by the explicit assignment of salience to rules. The main problem associated with explicitly using salience while you were just starting to learn CLIPS is the tendency to overuse salience and write sequential programs. This overuse defea ts the whole purpose of using a rule-based language, which is to provide a natural vehicle for those applications best represented by rules. In the same way, procedural languages are best for strong control-oriented applications, while object-oriented languages are best for representing objects. CLIPS has keywords called declare salience which can be used to explicitly set the priority of rules.

Salience is set using a numeric value ranging from the smallest value of - 10000 to the highest of 10000. If a rule has no salience explicitly assigned by the programmer, CLIPS assumes a salience of zero. Notice that a salience of zero is midway between the largest and smallest salience values. A salience of zero does not mean that the rule has no salience but, rather, that it has an intermediate priority level.

CLIPS provides some procedural programming structures that can be used on the RHS. These structures are the while and if then else that also are found in modern high-level languages such as Ada, C, and Pascal.

Another useful function with (while) loops is the break which ends the currently executing (while) loop. The return function immediately ends the currently executing deffunction, generic function, method, or message-handler.

Any function may be called from the RHS, which greatly contributes to the power of CLIPS. Many other CLIPS functions are available that may return with numbers, symbols, or strings. These functions may be used for their return values or for their side-effects. An example of a function only used for its side-effect is (printout). The value returned by the (printout) is meaningless. The importance of (printout) is in its side-effect of output. In general, functions may have nested arguments if appropriate to your desired effect.

Before a file can be accessed for reading or writing, it must be opened using the open function. The number of files that can be opened at once is dependent on your operating system and hardware. When you no longer need to access a file, you should close it with the close function. Unless a file is closed, there is no guarantee that the information written to it will be saved.

The logical name of a file is how CLIPS identifies the file. The logical name is a global name by which CLIPS knows this file in all rules. Although the logical name could be identical to the filename, you may want to use something different. Another advantage of a logical name is that you can easily substitute a different filename without making major program changes.

The function to read data from a file is the familiar (read) or (readline). The only new thing that you have to do is to specify the logical name from which to read as the argument of (read) or (readline).

To (read) more than one field, you must use a loop. Even with (readline), a loop is necessary to read multiple lines. A loop can be written by having one rule trigger another or with a while-loop. The loop should not try to read past the end of file or the operating system will issue an error message. To help prevent this, CLIPS returns an EOF symbolic field if you try to read past the end of file (EOF).

The evaluation function, eval, is used for evaluating any string or symbol except the "def" type constructs such as defrule, deffacts, etc., as if entered at the top- level. The build function takes care of the "def" type constructs. The (build) function is the complement of (eval). The build function evaluates a string or symbol as if it were entered at the top-level and returns TRUE if the argument is a legal def-type construct such as (defrule), (deffacts), and so forth.