In the last tutorial, you were introduced to the concept of wildcard pattern matching, in which the symbol ? is used to take the place of a symbol on the left hand side of a rule. Suppose we have the facts
(member-of beatles john_lennon paul_mccartney george_harrison ringo_starr) (member-of who roger_daltrey pete_townsend keith_moon) (member-of ebtg tracey_thorn ben_watt)
If we wish to write a rule which will be triggered by all three of these facts, we can't easily use the ? wildcard, because it will match only one symbol. Instead, we will use the multi-field wildcard, $?, which will match zero or more symbols, thus:
(defrule bands (member-of ?band $?) => (printout t "there is a band called " ?band crlf))
Produces the results:
CLIPS>(run) there is a band called ebtg there is a band called beatles there is a band called who
Taking this one step further, we can get a list of all members of all bands:
(defrule band-members (member-of ?band $? ?member $?) => (printout t ?member " is a member of " ?band crlf))
In the left hand side of this rule, the multi-field wildcard, $?, will match zero or more symbols. The wildcard ?member will only match one symbol at a time. Thus, the ?member wildcard will match once for each individual member of a band, while the $? wildcards match to the other preceding and following members. For example, the following table shows all the different ways this rule would match the facts for the Beatles:
Match# | first $? matches | ?member matches | last $? matches |
1 | nothing | john_lennon | paul_mccartney george_harrison ringo_starr |
2 | john_lennon | paul_mccartney | george_harrison ringo_starr |
3 | john_lennon paul_mccartney | george_harrison | ringo_starr |
4 | john_lennon paul_mccartney george_harrison | ringo_starr | nothing |
You can also use multi-field wildcard variables:
(defrule band-members (member-of ?band $?members) => (printout t "The members of " ?band " are " $?members crlf))
So far, the only way we've seen of incorporating a variable in a rule is to create it as part of a pattern on the left hand side. Using the bind function, it's also possible to create a temporary variable in the right hand side of a rule:
(defrule addup (number ?x) (number ?y) => (bind ?total (+ ?x ?y)) (printout t ?x " + " ?y " = " ?total crlf) (assert (total ?total)))
You should be aware that the temporary variable only exists inside the rule - don't expect to be able to use the value elsewhere. If you need variables which can be used by more than one rule or function without losing their values, you need to declare them using the defglobal construct in a file, like in this example:
(defglobal ?*var1* = 17 ?*oranges* = "seven" )
After a (reset), there will be two global variables, ?*var1* and ?*oranges* (the asterisks are necessary) with the values 17 and "seven" respectively, which may be accessed by any rule. To change their values, the bind function can be used.
Rules and facts, while offering great flexibility, are not suited to all
tasks. CLIPS offers a full range of procedural programming functions as
well.