RuleWorks

RuleWorks

Rules' Left-Hand Sides: Condition Elements

The left-hand side (LHS) is the "if" part of a rule. It specifies the conditions in working memory that must be true before the rule can fire. The LHS is composed of condition elements, each of which can match objects of a particular class (and its subclasses, if any). Condition elements can also test for particular attribute values, and bind variables to those values. The variables can be used only later in the same rule.

Condition elements (CEs) must be enclosed in parentheses. CEs can be positive, stating that certain conditions are true; or negative, stating that certain conditions are false. A negative CE has a minus sign (-) before its opening parenthesis. The first CE on an LHS must be positive.

RuleWorks performs an implicit conjunction (a logical AND operation) on all the CEs on an LHS. A rule is eligible to fire when there are objects that match all of its positive CEs, and there are no objects that match any of its negative CEs. It is also possible to specify a disjunction (a logical OR operation) of CEs.

Matching the Object Class

The first item in each CE must be the name of a declared object class. The class name is the only required item. In the example below, the CE (start) matches the object made in the ON-ENTRY statement.

Example 3-1. Object Class - ON-ENTRY

(entry-block main)

    (object-class start)

    (on-entry (make start))

    (rule say-hello

      (start)

      -->

      (write |Hello, world!|)

    )

(end-block main)

Example 3-2 shows some OBJECT-CLASS declarations from the sample configuration program, KIWI.RUL.

Example 3-2. Sample OBJECT-CLASS Declarations

(object-class part

    ^part-number

    ^name

    ^price

    ^is-expanded

)

(object-class option

    (inherits-from part)

)

(object-class hardware-option

    (inherits-from option)

    ^takes-slot

    ^in-slot

    ^is-placed

)

(object-class memory

    (inherits-from hardware-option)
)

When the class name in a CE is a parent class, objects of any of its inheriting subclasses can also match the CE. Given the OBJECT-CLASS declarations in Example 3-2, Table 3-1 shows which objects match each class. For example, an object of class MEMORY can match a CE that specifies any of the declared class names shown, but only an object of class PART can match a CE that specifies the class name PART.

Table 3-1. Matching Object Classes

An Object of Class Matches a Condition Element of Class
PARTOPTIONHARDWARE
-OPTION
MEMORY
PARTYesNoNoNo
OPTIONYesYesNoNo
HARDWARE-OPTIONYesYesYesNo
MEMORYYesYesYesYes

An object of any visible user-declared class matches a CE that specifies the built-in top-level class $ROOT.

Rules can refer only to classes that are visible to the entry block or rule block that contains them. See Chapter 5 for information on making object classes visible to more than one block.

Rules can refer to a parent class or to a specific subclass. In Example 3-3, the rule VERIFY-CONFIGURATION:APPLICATION-NEEDS-KIWOS applies to any one of the software applications: KiWindows, KiwiCalc, or KiwiTalk. Conversely, rule VERIFY-CONFIGURATION:KIWITALK-NEEDS-NETWORK applies only to KiwiTalk, not to KiWindows or KiwiCalc.

Example 3-3. Object Class Match Rules

(rule verify-configuration:application-needs-kiwos

    (active-context ^name verify-configuration)

    (kiwos-application ^$instance-of <applic>)

    - (kiwos)

    -->

    (make error ^severity warning ^message |Missing operating system|)

    (write (crlf) |Caution: to run application:| <applic>

      (crlf) |you need to have KIWOS, but you don't have KIWOS.|

      (crlf) |Fixup: adding KIWOS to your order.|

      (crlf)

    )

    (make kiwos)

)

(rule verify-configuration:kiwitalk-needs-network

    (active-context ^name verify-configuration)

    (kiwitalk)

    - (network-interface)

    -->

    (make error ^severity warning ^message |Missing network interface|)

    (write (crlf) |Caution: you ordered KiwiTalk, but don't have|

      (crlf) |the network interface hardware to use KiwiTalk.|

      (crlf) |Fixup: adding a network interface to your order.|

      (crlf)

    )

    (make network-interface)

)

Note: Any attribute accessed in a rule must either be declared in the class itself or be inherited from a parent class. RuleWorks generates a compile-time error if this requirement is not followed.

Writing Attribute-Value Tests

After the required class name, a CE can contain any number of attribute-value tests. These are the patterns that objects in working memory must match in order for the rule to fire. Attribute-value tests consist of an attribute name, a predicate, and a value. You can make combinations of tests on a single attribute by using conjunctions and disjunctions, which are similar to AND and OR operators.

The number of attribute-value tests on the LHS determines the test specificity of a rule. Test specificity is one of the principles used during conflict resolution (see Test Specificity).

Attribute Names

The attribute name in an attribute-value test must be one of the following constructs:

  • the name of an attribute declared in the CE's object class (or declared in an ancestor of that object class)
  • the name of a built-in attribute

The attribute operator (^) must precede the attribute name.

Given the declarations in Example 3-2, the following are valid CEs:

    (part ^part-number KI-9200)

    (option ^part-number KI-9200)

    (hardware-option ^part-number KI-9200 ^takes-slot yes)

    (memory ^part-number KI-9200 ^takes-slot yes)

The following CEs are not valid because the attributes have not been declared for the object classes:

    (part ^takes-slot no)

    (option ^is-placed yes)

The built-in attribute names are ^$INSTANCE-OF (see Matching the Exact Class and Matching the Instance Identifier).

Matching the Exact Class

If you want to match, or bind a variable to, the exact subclass of which an object is an instance, you can use the built-in, read-only attribute ^$INSTANCE-OF. In the following code fragment, the second CE binds the value of the ^$INSTANCE-OF attribute to the variable <OPTION>. This variable is then used in the MODIFY action.

Example 3-4. Matching the Exact Class

...

    (box ^$id <the-box> ^card-in-slot {[=] <len> [<] 8})

    (hardware-option ^$id <the-option> ^$instance-of <option>

      ^takes-slot yes ^is-placed no )

    -->

    (modify <the-box>

      ^card-in-slot [(<len> + 1)] <option>

      ^card-in-slot-obj-id [(<len> + 1)] <the-option>)

    ...

In this example, <OPTION> might be bound to the symbol MEMORY.

Matching the Instance Identifier

The built-in, read-only attribute ^$ID stores the INSTANCE-ID value of the object. If you want to copy, modify, or remove an object on the RHS, you usually first bind a variable to its identifier on the LHS. Such a variable is called a $ID variable. $ID variables give you a handle on the object that matched a CE. $ID variables in RuleWorks are similar to element variables in previous versions of OPS.

You can use variables bound to values of type INSTANCE-ID as pointers to maintain links between objects. Example 3-5 shows a RuleWorks program that uses such pointers to build a doubly linked list of objects. Note that the variables <FIRST-DATA> and <LAST-DATA> are $ID variables, but the variable <PREVIOUS-DATA> is not. Variable <NEW-DATA>, which is bound on the right-hand side, is not a $ID variable. Variables <PREVIOUS-DATA> and <NEW-DATA> cannot be used in MODIFY actions as variables <FIRST-DATA> and <LAST-DATA> are.

Example 3-5. Using $ID Variables as Pointers

(entry-block pointer-demo)

    (object-class data

      ^previous ; pointer to preceding datum

      ^next ; pointer to following datum

      ^value) ; actual datum

    (object-class pointer

      ^max-length ; desired length of list

      ^list compound) ; pointers to all DATA objects

    (on-entry

      (watch all)

      (make pointer ^max-length 7) ; arbitrary choice

      (make data ^previous nil ^next nil)) ; start with null pointers

    (rule init-list

    ;

    ; Initialize the list by making a DATA object with pointers to itself

    ;

      (pointer ^$id <the-pointer>)

      (data ^$id <the-data> ^previous nil ^next nil)

      -->

      (modify <the-data> ^previous <the-data> ^next <the-data> ^value 1)

      (modify <the-pointer> ^list (compound <the-data>)))

    (rule make-list

    ;

    ; Make a new datum and add it to the list

    ;

      (pointer ^$id <the-pointer>

        ^max-length <max>

        ^list {list [<] <max>} ;length of list is less than <MAX>

        ^list[1] <first-data> ; bind ID of first DATA

        ^list[$last] <last-data>) ; bind ID of last DATA

      (data ^$id <first-data> ;test ID of first DATA

        ^previous <previous-data>) ;bind backward ptr of first item

      (data ^$id <last-data> ; test ID of last DATA

        ^value <val> ; bind value of last item

        ^next <the-data>) ; bind forward ptr of last item

      -->

      (bind <new-data> ; bind a pointer to a MAKE action

      (make data ^previous <last-data>

        ^next <first-data>

        ^value (<val> + 1)))

      (modify <first-data> ^previous <new-data>)

      ;reset pointers to new item

      (modify <last-data> ^next <new-data>)

      (modify <the-pointer> ^list (compound list <new-data>)))

    (end-block pointer-demo)

    Using a Bound Variable to Select an Attribute

    You can use a bound variable to select an attribute on both the LHS and the RHS. This is useful if a program is treating object data as a look-up table.

    RuleWorks allows a variable-selected attribute to be matched against; the variable selecting the attribute must be bound before the match reference is made, and the value must be the name of an attribute in that object class (see Example 3-6). RuleWorks issues a run-time warning if the attribute binding does not exist in the specified object class.

    Example 3-6. Variable Selection of Attributes Variable Selection of Attributes

    (OBJECT-CLASS person

      ^name

      ^age

      ^phone_number

      ^address)

     

    (OBJECT-CLASS seek_slot ^slot_to_find)

     

    (make person

      ^name |Joe Bloggs|

      ^phone_number 01292-474382

      ^address |17 Anderson Crescent|)

     

    (make person

      ^name |John Doe|

      ^phone_number 0141-887-2456

      ^address |56 Alloway Drive|)

     

    (make seek_slot

      ^slot_to_find phone_number

      ^name Dorothy)

     

    (rule find_arbitrary_slot_datum_given_name

      (seek_slot ^slot_to_find <slot> ^name <person_name>)

      (person ^name <person_name> ^<slot> <datum>)

      -->

      (write (crlf) |Person| <name> |slot| <slot> |has value| <datum>)

    )

    Predicates

    The test part of an attribute-value test is a predicate that specifies the comparison to be performed (equal to, greater than, and so on) between the atom or atoms in the attribute and the scalar or compound value in the CE.

    Every value specified in a condition element is preceded by a predicate, either implicitly or explicitly. Values that you specify without a predicate are implicitly preceded by the identity predicate (see Identity, Equality, and Similarity Predicates)

    Predicates and Data Types

    Table 3-2 shows the match predicates of RuleWorks, with the types of attributes and values to which they can be applied. The values referred to in the third column of Table 3-2 can be constants, variables, or other expressions.

    While matching condition elements to working memory, RuleWorks performs automatic data type coercion for "reasonable" attribute-value tests only. Table 3-2 also shows which data types are reasonable for each match predicate. All other mixed-type comparisons yield "no match." "No match" is not an error condition; it merely means that the query "A relation B" will fail, no matter which relation was requested (greater than, less than, equal to, and so on). For example, the following test, which will never match, produces a warning message at compile time, and no message or error condition at run time:

    ^$ID < x

    Table 3-2. RuleWorks Match Predicates*

    Attribute
    Domain
    Predicate Value
    Domain
    Test
    ANY==ANYIdentity: Same type as and equal to
    (This predicate is optional in LHS attribute-value tests. It is required in RHS relational expressions.)
    ANY<>ANYNonidentity; converse of identity
    ANY=ANYEquality: Identical or equivalent numbers; identical symbols except for case; identical values of all other data types
    ANY-=ANYInequality; converse of equality
    ANY~=ANYSimilarity: Equal or phonetically similar symbols; equal or approximately equal numbers; identical values of all other data types
    ANY-~=ANYDissimilarity; converse of similarity
    NUMBER>NUMBERGreater than
    SYMBOL>SYMBOLLexicographically after
    ANY>=ANYGreater than or equal numbers; lexicographically after or equal symbols; identical values for all other data types
    NUMBER<NUMBERLess than
    SYMBOL<SYMBOLLexicographically before
    ANY<=ANYLess than or equal numbers; lexicographically before or equal symbols; identical values for all other data types
    ANY<=>ANYSame type
    ANY<->ANYDifferent type
    ATOM[+]COMPOUNDContainment; atom is contained within the compound
    ATOM[-]COMPOUNDNon-containment; converse of containment
    COMPOUND[+]ATOMContainment; compound contains atom
    COMPOUND[-]ATOMNon-containment; converse of containment
    COMPOUND[=]INTEGERLength equal
    COMPOUND[<>]INTEGERLength not equal
    COMPOUND[>]INTEGERLength greater
    COMPOUND[>=]INTEGERLength greater then or equal
    COMPOUND[<]INTEGERLength less than
    COMPOUND[<=]INTEGERLength less than or equal

    * Scalar predicates are those that are valid only for scalar attributes. The exceptions are identity and nonidentity (== and <>), which are also valid for comparing a compound attribute to a compound value. Compound predicates are those that are valid for compound attributes.

    For example, the seventh row in Table 3-2 states that any NUMBER, either FLOAT or INTEGER, can reasonably be tested for being greater than any other NUMBER. Thus, assuming the value of ^LIST-PRICE is the FLOAT 29.95, the following test returns TRUE:

    ^list-price > 20

    The same row also states that the greater-than predicate can be applied to two symbols. For example, the following test is valid, and the symbols are compared according to the lexicographic collating sequence for the target platform (the operating system and hardware where the executable image will run). Assume that ^ANIMAL is bound to the symbol AARDVARK, and the following test produces a match:

    ^animal < zebra

    A mixed type comparison such as the following generates a compile-time warning and fails to match at run time:

    ^$ID < 42

    Identity, Equality, and Similarity Predicates

    The identity predicate tests that values are of the same data type and also equivalent to each other. It executes faster than the equality predicate, which ignores data type and tests only for equivalent value. Table 3-3 compares results of using the identity, equality, and similarity predicates on a FLOAT of unequal value, a FLOAT of equal value, and an INTEGER of equal value.

    Table 3-3. Identity, Equality, and Similarity Predicates

    Match if ^X Attribute is...
    PredicateExample11.912.012
    Identity^x 12NoNoYes
    Equality^x = 12NoYesYes
    Similarity^x ~= 12YesYesYes

    The identity and nonidentity (<>) predicates are the only ones that can be applied when the attribute and the value are either both scalar or both compound.

    The identity and length-equal predicates are the only ones that can precede the first occurrence of a variable, because they can be used to bind a variable to the value of an attribute or to the length of a compound attribute. The first appearance of a variable on an LHS is where it is bound to a value. The identity predicate is the only one that can precede a disjunction of values (see Disjunctions of Values).

    Specifying Values

    Chapter 2 covers the general rules for specifying values in RuleWorks. This section explains the restrictions on constants, variables, and function calls in value expressions on the LHS.

    Constants

    Constants can be symbols, integers, or floating-point numbers. There are also two special constants: the instance identifier #0, which never refers to an actual WMO; and the opaque value %x0, which corresponds to the null pointer provided by most other programming languages.

    You cannot use any other INSTANCE-ID or OPAQUE constant in source code; use them only in the command interpreter.

    Variables

    The first time a variable appears on an LHS, the variable is bound to the attribute value in the object that matches the CE. All subsequent occurrences of that variable in that rule represent the same value. For example, suppose the LHS of a rule contains the following CEs:

      (memory ^price <price>) ; variable <price> is bound here

      (disk ^price > <price>) ; variable <price> is tested here

    If the run-time system finds a match for the first CE, the system binds the variable <PRICE> to the value stored in the ^PRICE attribute of the object of class MEMORY that matches the CE. Suppose the variable <PRICE> is bound to atom 129.95. Then the second CE is matched by an object of class DISK whose ^PRICE attribute has a value greater than 129.95.

    Function Calls

    You can use certain built-in or user-defined functions to specify a value in an attribute-value test. Functions used on the LHS must not change any objects in working memory and must always return the same result when called with the same arguments.

    The built-in RuleWorks functions that can be used on the LHS are listed in Table 3-4 and Table 3-5.

    Table 3-4. LHS Functions for Scalar Values

    NameDescription
    FLOATConverts a numeric value into a floating-point number.
    INTEGERConverts a numeric value into an integer.
    SYMBOLConverts any atom into a symbol.

    Table 3-5. LHS Functions for Compound Values

    NameDescription
    LENGTHReturns the number of elements in a compound value.
    NTHReturns the value of a specified element in a compound value.
    POSITIONFinds the first occurrence of an element in a compound value.

    You can use the functions shown in Table 3-4 to make sure an attribute-value test does not fail to match because the values are of different types. For example, assume there is an object of class BOX whose ^CARD-IN-SLOT attribute is three elements long. Assuming the variable <LIMIT> is bound to 5.5, the following CE does not match this BOX object because 5.5 is a floating-point number and the length predicates require integer operands:

      (box ^card-in-slot [<=] <limit>)

    However, the next CE does match:

      (box ^card-in-slot [<=] (integer <limit>))

    Function calls can include variables, but the variables must be bound to values. That is, the variables must have been used in a previous CE or previously in the same CE.

    The Quote Operator

    In attribute-value tests, you can quote values so that they are not evaluated. This allows you to use any symbol, operator, or variable as a constant atom.

    To quote a value, precede it with the quote operator (//). Using this operator is similar to enclosing an atom in vertical bars. For example:

      (memory ^price // <price>)

    The above CE matches an object whose class is MEMORY and whose ^PRICE attribute contains the symbol <PRICE>. If the value is quoted with vertical bars, the symbol is <price> in lowercase letters. If the quote operator is not there, the CE matches an object whose ^PRICE attribute contains the value bound to the variable <PRICE> (or, if <PRICE> is unbound, binds the variable to the value of the attribute).

    Accessing Compound Attributes

    You can test, match, and bind individual elements within a compound attribute, or the entire compound attribute. The attribute name without brackets indicates the entire compound value.

    A Single Element

    To test a single element of a compound attribute, use brackets ([]) and an index expression after the attribute name. This is called element-wise access into the compound attribute. The index expression must evaluate to an integer greater than zero. You cannot use unbound variables in the index into a compound attribute.

    The following example binds the variable <THIRD> to the third element of the compound attribute ^CARD-IN-SLOT:

      (box ^card-in-slot[3] <third>)

    If <THIRD> is already bound to the symbol KEYBOARD, the above example matches the object shown in Figure 2-6.

    Caution : When you use element-wise access into a compound attribute, it is possible for the binding instance of a variable to cause the match to fail. RuleWorks does not bind NIL or the declared FILL value (if any) when the index specified for an element-wise access points past the end of the compound attribute.

    In the above example, the match fails when ^CARD-IN-SLOT has fewer than three elements, even if the variable <THIRD> is unbound.

    The Last Element

    The special symbol $LAST represents the index of the last element. $LAST cannot be used as part of an expression. It must stand alone. The following example binds or tests the last element:

      (box ^card-in-slot[$last] <var>)

    Using $LAST for the index expression is an element-wise access. Therefore, the above CE never matches an empty ^CARD-IN-SLOT attribute.

    The Entire Compound

    To specify an entire compound attribute, use the attribute name with no index notation. This is called group-wise access.

    Note: When you use group-wise access of a compound attribute, the binding instance of a variable cannot cause the match to fail. If the attribute is empty, the variable is bound to the empty compound value ((COMPOUND)).

    In the following CE, the variable <CARDS> is bound to the entire compound attribute ^CARD-IN-SLOT:

      (box ^card-in-slot <cards>)

    If <CARDS> is already bound to a compound value, the CE above tests ^CARD-IN-SLOT and <CARDS> for identity on an element-by-element basis.

    You must use a compound value for group-wise access of a compound attribute. You cannot bind or compare an entire compound attribute to a scalar value.

    Predicates for Compound Attributes

    RuleWorks provides a number of predicates that operate on compound attributes only. These compound predicates allow various aspects of the entire set of values in a compound attribute to be tested within a condition element.

    The compound predicates start and end with brackets ([]). The characters within the brackets specify which test (greater than, less than, and so on) is used. Table 3-2 shows which predicates can be applied to compound values.

    Counting the Number of Elements

    RuleWorks provides six compound predicates for testing the number of values in a compound attribute: [=], [<>], [>], [>=], [<], and [<=]. An example attribute-value test:

      ^card-in-slot [>] 2 ; this test succeeds if the number of

              ; values in ^card-in-slot is greater than 2

    The length-equal-to compound predicate ([=]) allows the exact number of elements in the compound attribute to be either bound or compared for identity.

    Just as for the identity predicate, if the expression following the length-equal-to predicate is an unbound variable, then that variable is bound to the actual number of elements.

    If the expression following the length-equal-to predicate is a bound variable, a constant, or some other kind of expression, then the element count is tested for identity with the resulting value. The value must be an integer equal to or greater than zero.

    The following CE shows both uses of the length-equal-to predicate:

      (box ^card-in-slot [=] <len> ; bind the number of values in

                  ; ^card-in-slot to the variable <len>

      ^card-in-slot-obj-id [=] <len>) ;match only if the number of values

                  ;in ^card-in-slot-obj-id is the same

                  ;as in ^card-in-slot

    Testing for Emptiness

    Testing a compound value for emptiness is a common operation for stacks and queues. In RuleWorks this can be done by comparing the length of the compound attribute to zero. The following CE matches if the compound attribute ^CARD-IN-SLOT is empty:

      (box ^card-in-slot [=] 0)

    Searching for a Value

    Two predicates, containment ([+]) and non-containment ([-]), allow you to determine whether a compound attribute contains a specified element value. The following CE matches if the compound attribute ^CARD-IN-SLOT contains the value MEMORY:

      (box ^card-in-slot [+] memory) ;match if memory found

    The next CE uses the opposite test from the previous one:

      (box ^card-in-slot [-] memory) ;match if memory is NOT found

    The containment predicates, unlike the other compound predicates, can also be used to compare a scalar attribute value and a compound value. For example:

      (box ^card-in-slot-obj-id <cards>)

      (memory ^$ID [+] <cards>)

    In the example above, the CEs match when the identifier of the MEMORY object is contained within the compound value of the BOX object's ^CARD-IN-SLOT-OBJ-ID attribute.

    By default, the containment predicates test for identity of the compound element and the value. You can specify a different test by placing a scalar predicate next to the containment predicate. The compound value is then searched for an element that satisfies the specified test and scalar value. Consider the following CE:

      (box ^power-consumed-per-slot [-] > 20)

    This could be pronounced as "There is a BOX object whose ^POWER-CONSUMED-PER-SLOT attribute contains no value greater than 20."

    The scalar predicate must be next to the scalar attribute or value.

    Functions for Compound Values

    You can use three built-in RuleWorks functions, LENGTH, NTH, and POSITION, with compound values on the LHS. You must pass a bound compound variable as the first argument to each of these functions. For example, the following CE matches a compound attribute ^CARD-IN-SLOT that has two consecutive elements equal to MEMORY:

      (box ^card-in-slot <cards>

        ^card-in-slot [((position <cards> memory) + 1)] = memory)

    The POSITION function above finds only the first occurrence of MEMORY in <CARDS>, so a rule that uses this CE fires only once even on a BOX object that has three or more consecutive MEMORY elements.

    It is more efficient to use the length predicates or an index into the compound attribute rather than the LENGTH or NTH functions. For example, the following condition elements are equivalent:

      (box ^card-in-slot <cards> ; bind a variable

        ^card-in-slot-obj-id [=] (length <cards>)) ; test with a function

      (box ^card-in-slot [=] <len> ; bind a variable

        ^card-in-slot-obj-id [=] <len>) ; test with a predicate

    The second CE, using the length-equal predicate to bind the length of the first compound attribute, is more efficient than the first CE.

    Conjunctions of Tests

    A conjunction is a series of tests that must all be true of a single attribute in an object in order for the match to succeed. A conjunction is similar to a logical AND. You specify a conjunction by enclosing the tests in braces ({ }). The following CE matches when there is an object of class HARDWARE-OPTION whose ^PRICE attribute has a value between 100.00 and 500.00:

      (hardware-option ^price { > 100.00 < 500.00 } )

    If you want to bind as well as test the value of an attribute, you can use a conjunction. For example:

      (hardware-option ^in-slot { <slot-num> <> nil} )

    is equivalent to:

      (hardware-option ^in-slot <slot-num> ^in-slot <> nil )

    If there is an object whose class name is HARDWARE-OPTION or any subclass of HARDWARE-OPTION and whose ^IN-SLOT attribute has a value not equal to NIL, a match results. The run-time system then binds the variable <SLOT-NUM> to that value.

    Conjunctions of compound predicates can also be applied to compound attributes. For example:

      ^card-in-slot {<cards> [+] memory [>] 2 [=] <num-cards> }

    Assuming both variables were unbound, the above conjunction first binds the entire compound value to the variable <CARDS>. The next two tests produce a match if ^CARD-IN-SLOT contains at least one MEMORY and has more than two elements. Finally, the above binds the number of elements in ^CARD-IN-SLOT to the variable <NUM-CARDS>.

    Disjunctions of Values

    A disjunction is a list of values any one of which can match the value of an attribute in an object in order for the match to succeed. A disjunction is similar to a logical inclusive OR. You specify a disjunction by enclosing the list of values between double angle brackets (<< >>). You must not specify any predicate with a disjunction of values: the implicit identity predicate is the only valid predicate for a disjunction.

    The following CE contains a disjunction:

      (hardware-option ^takes-slot << no false nil >> )

    Note that you must put at least one white space character around each side of the double angle brackets.

    Variables or any other value expressions you specify in a disjunction are evaluated. Consider the following disjunction:

      ^number << 45 <number> >>

    This matches if the ^NUMBER attribute is 45 or it is identical to the binding of <NUMBER>. The next example shows another valid test containing a function call:

      ^number << 45 <number> (length list) >>

    Negative Condition Elements

    Negative CEs specify that no object that matches their pattern exists in working memory. Negative CEs can contain any attribute-value test allowed in positive CEs. You specify a negative CE by putting a minus sign (-) in front of it. Consider the rule CHOOSE-SLOTS:PLACE-NONMEMORY from the sample configuration program.

    Example 3-7. Negative Condition Elements

      (rule choose-slots:place-nonmemory

        (active-context ^name choose-slots)

        (box ^$id <the-box> ^card-in-slot { [=] <len> [<] 8 })

        (hardware-option ^$id <the-option>

          ^takes-slot yes

          ^$instance-of <option>

          ^is-placed no)

        - (memory ^$id <the-option>)

        - (memory ^is-placed no)

        -->

        (modify <the-box>

          ^card-in-slot [(<len> + 1)] <option>

          ^card-in-slot-obj-id [(<len> + 1)] <the-option>)

        (modify <the-option> ^is-placed yes ^in-slot (<len> + 1)))

    The purpose of this rule is to assign slots to options that are not memory after all the memory cards have been placed in a slot. The first negative CE specifies that the object that matched the third positive CE is not of class MEMORY. The second negative CE specifies that there exists no object of class MEMORY with ^IS-PLACED attribute equal to NO. In other words, it specifies that all objects of class MEMORY have been placed.

    Disjunctions of Condition Elements

    A disjunction of condition elements is a group of condition elements enclosed in double angle brackets (<< >>). The entire disjunction matches when any one of the condition elements matches.

    In general, a rule that contains CE disjunctions is equivalent to a collection of rules, each of which corresponds to a particular branch through each disjunction. A rule with a 2-CE disjunction and a 3-CE disjunction has 2 * 3 = 6 possible branches. Although you could express exactly the same concepts with 6 rules, by using the CE disjunctions your code becomes more compact and more easily maintained.

    The following rule:

      (rule test-ce-disjunctions

        << (obj-class-1 ^attr-1 <x>)

        (obj-class-2 ^attr-2 <x>) >>

        -->

        (write (crlf) |Value of attribute is| <x>))

    is equivalent to the following two simple rules:

      (rule |test-ce-disjunctions;1|

        (obj-class-1 ^attr-1 <x>)

        -->

        (write (crlf) |Value of attribute is| <x>))

    and:

      (rule |test-ce-disjunctions;2|

        (obj-class-2 ^attr-2 <x>)

        -->

        (write (crlf) |Value of attribute is| <x>))

    There is a restriction on the use of CE disjunctions: if a variable is bound in one branch of a CE disjunction, and the variable appears later on in the rule, then it must be bound in all branches of the CE disjunction. A variable <X> can be bound in only one branch of the disjunction provided that <X> does not subsequently appear elsewhere in the rule.

    It is not recommended that you do not write CE disjunctions such that the same object can match more than one branch. Doing so causes the rule to fire more than once on the same object, a result you probably would not expect.

    When a rule contains a disjunction of CEs (see Disjunctions of Condition Elements) the test specificity of each instantiation is calculated separately (see Test Specificity). The class specificity of each instantiation is calculated separately, too.

    Test Specificity

    If two or more instantiations in the conflict set have equal priority after refraction, recency, and class specificity have been applied (see Chapter 1), RuleWorks orders the instantiations according to their test specificity. An instantiation's test specificity is determined by the number of attribute-value tests in the left-hand side of the rule to which the instantiation refers.

    Each of the following items adds one to the test specificity of a rule:

    • An object class name. The class name counts as one even if the condition element is negated.
    • A disjunction of values. The entire disjunction counts as one test no matter how many values it contains.
    • A predicate followed by any value expression, except an unbound variable or within a disjunction of values. This includes the implied identity predicate.

    In a conjunction of tests, each test adds one to the test specificity, subject to the restrictions above. For example, assuming the two variables are not bound, the following CE contains only two tests:

      (box ^$id <the-box> ^card-in-slot { [=] <len> [<] 8 })

    The two tests in this CE are the class name BOX and the length-equal predicate followed by the value 8. The two variables are unbound, so their presence does not add to the test specificity.

    Note: The following items do not count towards test specificity:

      • An explicit attribute name
      • The binding instance of a variable
© RuleWorks.co.uk | | Sitemap