This page contains some very brief and basic information about the CLIPS expert system shell, a public domain program originally developed at NASA. It will cover only some of the basic aspects of CLIPS. For example, it ignores objects, modules, and global variables. Although there are GUI interfaces to CLIPS, only the command-line interface is covered here. To use this interface, run the clips program. You will get a prompt where you can type in commands or expressions to be evaluated. CLIPS uses a LISP-like syntax in which everything (other than symbols and numbers) is a list, and expressions are in prefix form. For example, the list(parent tim mary)
might be used to represent the fact that tim is a parent of mary. And the expression that computes the sum of 2 plus 2 is written as
(+ 2 2)
More generally, when a list is used to represent a function or command, the first item in the list is the name of the function or command, and the remaining items, if any, are arguments. For example, the command for exiting from clips must be typed in parentheses: (exit). Any time you type something at the clips command prompt, its value will be printed. To get the sum of 2 plus 2, all you have to do is type (+ 2 2) at the command prompt. Even commands have values (often just TRUE or FALSE) which are printed after the command has been executed. (In fact, a command is really just a function that is evaluated for its side-effects.)
You can type an entire program at the clips command prompt, but it is more common to create the program in a file and load the file from the command prompt. The command for doing this is load, with the file name as a parameter. For example, to load a file named buckets.clips, you would enter:(load "buckets.clips")
(The quotation marks around the file name are optional, as long as the file name does not contain any special characters.) After loading a program, you will generally run it with two commands: (reset) followed by (run). If you want to run the program again, you will have to do a (reset) first. You could try this with the sample file buckets.clips, which contains a program for solving a certain simple puzzle.
Full information about CLIPS can be found in the Basic Programming Guide, which can be found in PDF format in the file bpg.pdf.
The basic or atomic syntax elements of clips include numbers and symbols. Symbols can include most printable characters, but not parentheses, <, ?, $, ~, |, &, and semicolon. (Actually, $, ?, and < can occur inside symbols, but not at the beginning.) Semicolons are used to start comments, which end at the end of the line. Symbols are case-sensitive. Strings consist of any characters between double quotes. There are some other special-purpose atomic data types, such as "fact addresses".
A fact can be "ordered" or "unordered". An ordered fact is a list of atomic (that is, non-list) items. The first item must be a symbol (not including test, and, or, not, declare, logical, object, exists, or forall). For example, "(parent tim mary)", "(start), and "(data 12 17 15 42)" are ordered facts.
Unordered facts are based on "templates" and have values for named "slots" that are specified in the templates. The first item in an unordered fact is the name of the template on which it is based. Remaining items are lists that give values for named slots in the template. For example,(family (father tim) (mother ann) (child mary))
is an unordered fact based on a template named "family". That template has slots named "father", "mother", and "child". This fact is an instance of the template in which the slots have the values "tim", "ann", and "mary". (A template can be created using the deftemplate command.) Although template facts are very useful, I will not consider them further here. I will note that the object system in CLIPS is essentially templates with inheritance and methods -- it seems to amount, more or less, to an implementation of Minsky's idea of frames as knowledge representation, complete with daemons.
The working memory in CLIPS includes a list of facts. A fact can be entered into working memory by asserting it with the assert command. For example:(assert (parent tim mary))
A listing of the facts in working memory can be obtained by entering the (facts) command. Every fact in the list has an index. A fact can be removed from working memory by giving its index to the retract command. For example,(retract 17)
will remove the fact with index 17 from the list. This does not renumber the subsequent facts; there will simply be a gap in the indices. The (reset) command will clear working memory (as well as several other things) and will do some initialization. It always asserts the fact (initial-fact) as rule number zero. You can arrange for some other facts to be asserted by the (reset) command by putting them in "deffacts" construct. A deffacts construct consists of the command deffacts, a name for the construct, and a list of facts. For example(deffacts family-tree (parent tim mary) (parent ann mary) (parent bob ann) (parent fred bob) )
A deffacts construct is usually part of a program, and it represents permanent or background knowledge necessary for the program. The (reset) command will put these background facts into working memory. When the program is run, it will use these facts along with any other facts that it deduces.
The major part of a typical CLIPS program consists of rules. Each rule specfies conditions and actions. Conditions are patterns which are matched against facts in working memory. Actions are commands. When the conditions of a rule match the current state of working memory, it is said to be activated and it is put on the agenda, which is simply the list of currently activated rules. An activated rule is eligible to be executed, but it is not actually executed until it gets to the top of the agenda. When that happens, the rule fires -- that is, its actions are executed. This can result in new facts being asserted, which can cause new rules to be be activated and placed on the agenda. Also, facts can be retracted, which might deactivate some of the active rules because their conditions are no longer satisfied. These rules are removed from the agenda. The (reset) command clears the current agenda; when the initial set of facts is then asserted, some rules might become active and be placed on the agenda. The (run) command causes rules to be fired, with any resulting changes in the agenda, until the agenda is empty (or until the firing of a rule causes a (halt) command to be executed as one of its actions).
The ordering of rules on the agenda determines the order in which they will be fired. Newly activated rules are not necessarily placed at the end of the agenda. First of all, each rule can have a salience, which is an integer in the range -10000 to 10000. Rules of higher salience are always placed above rules of lower salience on the agenda, and therefore are executed first. Salience allows you to exert some influence over the order in which rules will be applied. For example, suppose your program occasionally produces some values for output, and suppose you want to make sure that a value is output as soon as it is generated. You could give your output rules higher salience than other rules. When an output rule is activated because some output has become available, its higher salience will ensure that it is placed at the top of the agenda and is executed immediately. Salience could also be used to encode heuristic knowledge about which rules should be tried first.
Within rules of the same salience, the placement of rules on the agenda is determined by the conflict-resolution strategy. The default strategy is depth, which means that new rules are placed above rules that are already on the agenda. Another strategy is breadth, which places newly activated rules below existing rules. These two strategies result, respectively, in a depth-first or breadth-first search. To change from the default depth-first strategy to breadth-first, use the command:(set-strategy breadth)
There are other strategies, but depth and breadth are probably the most common.
(Note: A rule can appear on the agenda several times simultaneously. It appears once for each fact or combination of facts that are matched by its patterns. Note also that a given match can only cause the rule to fire once. This is one reason why the facts are ordered and numbered. You can think of the rule as marching down the list of facts, not looking back after it fires. If you retract and then re-assert a fact, it gets a new number and can be used in new matches.)
A rule is created using a defrule construct. The syntax for a defrule is:(defrule <name-of-rule> <optional-declare> <conditions> => <actions> )
Indentation and line-breaks are not significant to the computer. Every rule must have a name, which can be any symbol. The <optional-declare> part can be used to specify the salience of the rule. This has a format such as(declare (salience 17))
to declare that the salience of the rule is 17. If no salience is declared, then the salience of the rule is 0. The <conditions> are patterns that are matched against facts in working memory. There can be zero or more of them, but if there are zero, the computer automatically inserts the pattern "(initial-fact)". The <actions> are the commands that will be executed when the rule fires. The actions often include assert and retract commands, but any command can be an action. There can be any number of actions. The "=>" is a special symbol that separates the actions from the conditions.
Defrules are part of a CLIPS program and are generally defined in a file. The (reset) command does not erase rules.
The first item in a pattern must be a symbol. The remaining items can be more complicated. An item that is a symbol or number matches the same symbol or number in a fact. An item can also be a variable. A variable name consists of a ? followed by one or more additional characters, such as ?x or ?fred. (You use ? by itself in a pattern, but it represents an anonymous variable or wildcard. Two ?'s in the same rule do not necessarily represent the same value.) A variable gets a value through matching. That is, matching a pattern against a fact will assign a value to any unbound variable in the pattern. The value can then be used in later matches or in actions. For example, consider the rule:(defrule grandp (parent ?x ?y) (parent ?y ?z) => (assert (grandparent ?x ?z)) )
If you add a $ to the front of a variable in a pattern, then that variable will match a list of zero or more items in a fact. In clips, such a list is called a multifield value. For example, the pattern (data $?vals) will match the fact (data 10 17 83) and will assign the value (10 17 83) to the variable ?vals. (Note that the $ is not part of the variable name.)
It is possible to put constraints on the values that will match an item in a pattern. In fact, a constant value is simply a constraint that will only match a specific value. A variable is also a constraint which matches anything if the variable is unbound, or matches the value of the variable if it already has a value. The "|" operator can be used to make a constraint that will match one of several alternatives. For example, (color red|green|blue) will match any of the facts (color red) or (color green) or (color blue). The "~" operation can be used to match "anything but". For example, (color ~red) will match any color fact in which the color is not red. The & operator can be used to combine constraints, and in this case a variable name is considered to be a constraint. For example, (color ?c&~black&~white) will match any color fact in which the color is is not black and is not white. Furthermore, the value of ?c will be set to the value that matches.
The ":" operator can be used to constrain a match by calling a function. For the constraint to be satisfied, the function must return a non-FALSE value. This only makes sense when combined with a variable that can be used as an argument to the function. For example,(values ?a&:(> ?a 0) ?b&:(>= ?b ?a))
will match (values 10 25) but not (values 0 1) or (values 7 5). A function can also be called in a separate test pattern which succeeds if the function call returns a non-FALSE value. For example:(test (>= ?b ?a))
In addition to the operators for combining constraints, you can combine entire patterns with the not, and, and or operators. For example,(not (parent ?a ?b))
succeeds if there is no fact that matches (parent ?a ?b), and(or (grandparent tim ?) (grandparent ? tim))
will succeed if tim either is or has a grandparent.
The "=" operator, when used as a constraint, allows you to evaluate a function and use that value within a pattern. For example, suppose that variables ?a and ?b and you would like to match a fact that includes the sum of ?a and ?b. You can't do this with a pattern of the form(value (+ ?a ?b))
because there is nothing here that tells the computer to evaluate the sum before attempting the match. To tell it to do this, use "=" as follows:(value =(+ ?a ?b))
Finally, there is one more thing you need to know about patterns, if you want to be able to retract facts. The retract command takes the index of a fact as its argument. When a pattern matches a fact, it is possible to bind the index of that fact to a variable. Then, in the action section of the rule, you can use that variable in a retract command. To bind the address of a fact, use a pattern of the form "variable <- pattern". When this matched, the index of the fact that matches the pattern is assigned to the variable. For example, consider the rule(defrule delete-marriage (divorced ?a ?b) ?marriage <- (married ?a ?b) => (retract ?marriage) )
Suppose that tim and ann are married and that they get divorced. When this happens, the rule will fire with ?a equal to tim and ?b equal to ann. The index of the fact (married tim ann) will be assigned to ?marriage. This fact is then deleted by the action (retract ?marriage).
You can find examples of many of these features in buckets.clips.
The action part of a rule often includes assert and retract commands, but it can include any command.
The bind command can be used in the action part of a rule to assign a value to a variable. For example:(bind ?x (+ ?a ?b))
Another common command is printout, which is used to output some text. The first argument of a printout command should be the symbol t. (This stands for standard output; other symbols could appear here if the text is to be output to a file.) The remaining arguments are printed out. If an argument is a variable, it's value is printed. A function call will be evaluated and its value printed. The special symbol crlf can be used to output an end of line. For example:(printout t "The sum of " ?a " and " ?b " is " (+ ?a ?b) crlf)
The (halt) command can be used as an action to stop the further execution of rules. This command does not change the agenda or working memory, and you can resume execution of the program by entering the (run) command at the prompt.
The (read) function can be used to read in one item typed by the user. The item should be a number, symbol, or string. It is returned as the return value of the function. For example, to read the user's input into the variable ?x, use:(bind ?x (read))
There is also a (readline) function that will read one entire line of input from the user and will return the input as a string.
There are many built-in functions in CLIPS, including functions that act like while loops and if statements. See the CLIPS programmer's guide for more information. It is also possible to define your own functions in CLIPS, using the deffunction construct. The first argument to deffunction is the name of the function. This is followed by a list of variables, which are the parameters of the function. The remaining items in the function are function calls. The value of the last of these function calls is returned as the value of the function. Functions can be written just for their side effects and used as commands. For example:(deffunction family (?father ?mother ?child) (assert (parent ?father ?child)) (assert (parent ?mother ?child)) (assert (married ?father ?mother)) )
This function provides a shorthand for asserting some facts: (family tim ann mary).
If you want to use lists that can contain variable numbers of arguments, you will have to know more about multifield values. A multifield value is a list of atomic values (such as numbers, symbols, and strings). It is important to realize that CLIPS does not support nested lists in ordered facts or as values of variables. So, multifield values are treated in a special way when you use them to assert facts in the action part of a rule. Instead of adding the multifield value as a single item in the fact, it is "spliced" into the fact.
Recall that a multifield value can be matched to a variable in a pattern if a $ is added to the front of the variable. The value of the variable will be a list of zero or more items. If you want to match any fact that begins with the symbol input and contains the word father, for example, you could use the pattern(input $?before father $?after)
When this matches, ?before will be the list of words that precede father, and ?after will be the list of words that follow it. In the action part of the same rule, you might say(assert (before-father ?before))
If the value of ?before is (when I think of my), this would assert "(before-father when I think of my)" and not "(before-father (when I think of my))". The latter form would not even be a legal ordered fact.
There are several functions that can be used with multifield values. Most of them have names that end with $. Here are a few:
- (create$ X Y Z...) -- makes a multifield value from expressions X, Y, Z, ... If one of these expressions is itself a multifield value, it is spliced into the value of create$. Example: (create$ a b c) makes the multifield value (a b c).
- (nth$ N L) -- where N is an integer and L is a multifield value, returns the N-th item from L. Numbering starts from 0.
- (first$ L) -- is the same as (nth$ 1 L).
- (rest$ L) -- where L is a multifield value, returns L with its first element removed.
- (member$ X L) -- where X is any expression and L is a multifield value, checks whether X is a member of L. If X is found, the actual return value is the index of X in L.
- (subsetp L1 L2) -- where L1 and L2 are multifield values, checks whether L1 is a subset of L2.
- (explode$ str) -- where str is a string, creates a multifield value whose elements are the "words" in str. For example, (explode$ "I can't do that") produces the multifield value (I can't do that).
- (implode$ L) -- where L is a multifield value, creates a string in which the "words" are the elements of L.
The explode$ function can be used in combination with (readline) to read the user's input, convert it into a multifield value, and then assert a fact that contains the input words. For example, the following rule might be used as part of a conversation program written in CLIPS:(defrule converse ?t <- (talk) ; Fires when there is a (talk) fact. => (retract ?t) ; Get rid of (talk) fact. (bind ?input-string (readline)) ; Read a line from user. (bind ?input (explode$ ?input-string)) ; Convert it. (assert (input ?input)) ; Put input into working memory. )
Presumably, there would be other rules that would recognize patterns in the input and respond appropriately. You might want to use implode$ for printing multifield values, to avoid having parentheses in the output. (After responding, these rules could retract the "input" fact and re-assert the "talk" fact to trigger the next round of the conversation.)
For an example of a complete program that uses multifield values, see buckets_path.clips. This is an alternative version of buckets.clips.
We end with descriptions of some of the most useful commands in CLIPS. These are commands that are generally entered at the command line. Some of them have been mentioned above.
The command (load <file-name>) loads a file. The file should consist of "constructs" such as deffacts, defrule, and deffunction. Constructs from the file replace currently existing constructs of the same name, so it's usually OK to reload a file after editing it. The names of the constructs are listed as they are compiled. If an error is found in a construct, an error message will be printed for that construct in place of the name. The final value printed by this command is TRUE if the file loaded without error and is FALSE if some error occurred. The command (save <file-name>) will save all the currently defined constructs to a file. You can load a file that consists of facts with (load-facts <file-name>). If you have a file that contains commands, you can tell CLIPS to read it and execute the commands that it contains with the command (batch <file-name>).
The (clear) command will return CLIPS to its initial state. It is the same as exiting from CLIPS and restarting it. The (reset) command merely clears the agenda and working memory, then asserts the fact (initial-fact) and executes any deffacts constructs. Notice that a rule that uses (initial-fact) as a pattern is a good way to get your program started, since it will match working memory after any (reset).
The (run) command will cause the program to start execution. This command can take a parameter that specifies the maximum number of steps to execute. For example, (run 1) will cause one rule to fire and then return to the command prompt. This allows you to step through a program one rule firing at a time. And (run 10000) puts a limit of 10000 on the number of rule-firings before the program finishes or pauses. Don't forget that a (reset) is probably needed before (run) to prime working memory with some initial facts.
You can see the facts that are in working memory with the (facts) command. The (rules) command will list the names of any rules that have been defined. the (agenda) command will show you the list of rules that are currently on the agenda, along with the facts that caused them to be activated.
For debugging, you can have CLIPS print out some information as it is working by using the watch command. This command takes a parameter that says what type of thing you want to watch. The most useful parameters are: facts, rules, and activations. These will print out messages when a fact is asserted or retracted, when a rule is fired, and when a rule is activated. You can get all debugging messages by saying (watch all). You can turn off the messages with the unwatch command, which takes the same parameters as watch. You can insert breakpoints in a program. The command (set-break rule-name) sets a breakpoint for the rule named rule-name. The program will then pause just before that rule is about to be fired. Remove the breakpoint with (remove-break rule-name).
The set-strategy command is used to set the conflict resolution strategy. Possible parameter values include depth and breadth, with depth being the default. The return value of this command is the current strategy.
The (exit) command can be used to exit from CLIPS.
David Eck, February 2003