Search code examples
clipsexpert-system

How to further check fact values for only selected facts?


I have following template:

(deftemplate drule
         (slot name1)
         (slot id)
         (multislot field1)
             (multislot value1)
         (slot name2)
         (multislot field2)
         (multislot value2))

(deftemplate claim
         (slot name)
         (multislot field)
         (multislot value))

I have the following rule:

(defrule drule
  (drule
   (id ?id))
  (forall
   (drule
    (id ?id)
    (name1 ?name1)
    (field1 $?f11 ?field1 $?)
    (value1 $?v11&:(= (length$ ?f11)(length$ ?v11)) ?value1 $?)
    (name2 ?name2)
    (field2 $?f22 ?field2 $?)
    (value2 $?v22&:(= (length$ ?f22)(length$ ?v22)) ?value2 $?))
   (claim
    (name ?name1)
    (field $?f1 ?field1 $?)
    (value $?v1&:(= (length$ ?f1)(length$ ?v1)) ?value1 $?))
   (claim
    (name ?name2)
    (field $?f2 ?field2 $?)
    (value $?v2&:(= (length$ ?f2)(length$ ?v2)) ?value2 $?))
   (not
    (claim (field $?f3 ?field1 $?)(value $?v3&:(= (length$ ?f3)(length$ ?v3)) ~?value1 $?)))
   (not (claim (field $?f4 ?field2 $?)(value $?v4&:(= (length$ ?f4)(length$ ?v4)) ~?value2 $?))))
  (forall
   (claim
    (field $?f5 ?field5 $?)(value $?v5&:(= (length$ ?f5)(length$ ?v5)) ?value5 $?))
   (not
    (claim (field $?f6 ?field5 $?)(value $?v6&:(= (length$ ?f6)(length$ ?v6)) ~?value5 $?))))
=>
(assert (success)))

The above rule does the following:

  1. Check whether all fields:value pair in drule are found in claim facts.
  2. Check whether all field:value pair in drule are also the same in other matched claims. (only if field is found in the claim) answered
  3. Check whether all field:values pairs are same in each selected pair. (Only if field is found).

For example,

(assert
   (claim (name 'Employee') 
          (field 'EmpName' 'Company')
          (value 'Bob' 'ABC'))
   (claim (name 'Event')
          (field 'EmpName' 'EventName' 'Company')
          (value 'Bob' 'Conference' 'ABC'))
   (drule (id '001')
          (name1 'Employee')
          (field1 'Company')
          (value1 'ABC')
          (name2 'Event')
          (field2 'EventName')
          (value2 'Conference')))

Above should be successful, while the below should fail. Because EmpName mismatched.

(assert
   (claim (name 'Employee') 
          (field 'EmpName' 'Company')
          (value 'Bob' 'ABC'))
   (claim (name 'Event')
          (field 'EmpName' 'EventName' 'Company')
          (value 'Adam' 'Conference' 'ABC'))
   (drule (id '001')
          (name1 'Employee')
          (field1 'Company')
          (value1 'ABC')
          (name2 'Event')
          (field2 'EventName')
          (value2 'Conference')))

But my rule fails when there is the following assertion

(assert
   (claim (name 'Employee') 
          (field 'EmpName' 'Company')
          (value 'Bob' 'ABC'))
   (claim (name 'Event')
          (field 'EmpName' 'EventName' 'Company')
          (value 'Bob' 'Conference' 'ABC'))
   (claim (name 'Event')
          (field 'EmpName' 'EventName' 'Company')
          (value 'Adam' 'Conference' 'ABC'))
   (drule (id '001')
          (name1 'Employee')
          (field1 'Company')
          (value1 'ABC')
          (name2 'Event')
          (field2 'EventName')
          (value2 'Conference')))

I want to fire the rule even if there is one claim with same EmpName field. My rule checks all the claims and if there is any field:value mismatch then it wont fire.

EDIT: Is there a way to retrieve the field EmpName or other field like Company to the RHS? For example, for the last set of facts above, can I get the RHS to (assert (User ?name successfully entered)). I only want the for the claim which was matched in the forall (CLIPS throws me an error).

Thank you.


Solution

  • I'm not sure if this is precisely what you want in satisfying criterion #3 since I don't fully understand your explanation, but it works properly for the examples you've given.

    (defrule drule
       (drule (id ?id)
              (name1 ?name1)
              (name2 ?name2))
       (forall   
          (drule (id ?id)
                 (name1 ?name1)
                 (field1 $?f11 ?field1 $?)
                 (value1 $?v11&:(= (length$ ?f11)(length$ ?v11)) ?value1 $?)
                 (name2 ?name2)
                 (field2 $?f22 ?field2 $?)
                 (value2 $?v22&:(= (length$ ?f22)(length$ ?v22)) ?value2 $?))
          (claim (name ?name1)
                 (field $?f1 ?field1 $?)
                 (value $?v1&:(= (length$ ?f1)(length$ ?v1)) ?value1 $?))
          (claim (name ?name2)
                 (field $?f2 ?field2 $?)
                 (value $?v2&:(= (length$ ?f2)(length$ ?v2)) ?value2 $?))
          (not (claim (field $?f3 ?field1 $?)
                      (value $?v3&:(= (length$ ?f3)(length$ ?v3)) ~?value1 $?)))
          (not (claim (field $?f4 ?field2 $?)
                      (value $?v4&:(= (length$ ?f4)(length$ ?v4)) ~?value2 $?))))
       (forall
          (claim (name ?name3&?name1|?name2)
                 (field $?f5 ?field5 $?)
                 (value $?v5&:(= (length$ ?f5)(length$ ?v5)) ?value5 $?))
          (exists
             (claim (name ~?name3&?name1|?name2)
                    (field $?f6 ?field5 $?)
                    (value $?v6&:(= (length$ ?f6)(length$ ?v6)) ?value5 $?))))
       =>
       (assert (success)))
    

    For values you want to use on the RHS, you can retrieve them outside the forall conditional elements by either adding them to the drule pattern (as is done here for name1 and name2) or by adding an additional pattern for retrieving them from a specific claim fact.

    Since you've got some complex logic, you might find it easier to debug your code if you split the logic between multiple rules:

    (defrule check-1
       (drule (id ?id)
              (name1 ?name1)
              (field1 $?f11 ?field1 $?)
              (value1 $?v11&:(= (length$ ?f11)(length$ ?v11)) ?value1 $?)
              (name2 ?name2)
              (field2 $?f22 ?field2 $?)
              (value2 $?v22&:(= (length$ ?f22)(length$ ?v22)) ?value2 $?))
       (not (and (claim (name ?name1)
                        (field $?f1 ?field1 $?)
                        (value $?v1&:(= (length$ ?f1)(length$ ?v1)) ?value1 $?))
                 (claim (name ?name2)
                        (field $?f2 ?field2 $?)
                        (value $?v2&:(= (length$ ?f2)(length$ ?v2)) ?value2 $?))))
       =>
       (assert (mismatch ?id)))
    
    (defrule check-2
       (drule (id ?id)
              (name1 ?name1)
              (field1 $?f11 ?field1 $?)
              (value1 $?v11&:(= (length$ ?f11)(length$ ?v11)) ?value1 $?)
              (name2 ?name2)
              (field2 $?f22 ?field2 $?)
              (value2 $?v22&:(= (length$ ?f22)(length$ ?v22)) ?value2 $?))
       (or (claim (field $?f3 ?field1 $?)
                   (value $?v3&:(= (length$ ?f3)(length$ ?v3)) ~?value1 $?))
           (claim (field $?f4 ?field2 $?)
                  (value $?v4&:(= (length$ ?f4)(length$ ?v4)) ~?value2 $?)))
       =>
       (assert (mismatch ?id)))
    
    (defrule check-3
       (drule (id ?id)
              (name1 ?name1)
              (name2 ?name2))
       (exists (claim (name ?name3&?name1|?name2)
                      (field $?f3 ?field3 $?)
                      (value $?v3&:(= (length$ ?f3) (length$ ?v3)) ?value3 $?))
               (not (claim (name ~?name3&?name1|?name2)
                           (field $?f4 ?field3 $?)
                           (value $?v4&:(= (length$ ?f4)(length$ ?v4)) ?value3 $?))))
       =>
       (assert (mismatch ?id)))
    
    (defrule drule
       (declare (salience -10))
       (drule (id ?id))
       (not (mismatch ?id))
       =>
       (assert (success)))