Can I bind a variable on some condition? - clips

I have a 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))
Can I have rule which will
Check whether there are any claims with name same as the one in drule.
Check whether claim:field has value 'EmpName', if found then bind it to ?name.
Currently I have something as follows:
(defrule drule
(drule (id ?id)
(name1 ?name1)
(name2 ?name2))
(claim (name ?name1)
(field $?pf1 'EmpName' $?)
(value $?pv1&:(= (length$ ?pf1) (length$ ?pv1)) ?name $?))
(claim (name ?name2)
(field $?pf2 'EmpName' $?)
(value $?pv2&:(= (length$ ?pf2) (length$ ?pv2)) ?name $?))
...
=>
(assert (success ?name))
But it will bind only if all the selected claims have a field EmpName. I only want to check those claims with name
mentioned in drule. And if any of those claims have field 'EmpName' then it should be bound. If not just continue. My
rule fails if some claims do not have EmpName field at all. Assumption is that some claim will have EmpName field
and value.
Desired input and output:
(assert
(claim (name 'Employee')
(field 'Company')
(value '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')))
Output:
(success 'Bob')
Sorry if it is very stupid, I tried using test$ and member$ on field. But could not figure it out. I am self-learning CLIPS in my free time.

CLIPS>
(deftemplate drule
(slot name1)
(slot id)
(multislot field1)
(multislot value1)
(slot name2)
(multislot field2)
(multislot value2))
CLIPS>
(deftemplate claim
(slot name)
(multislot field)
(multislot value))
CLIPS>
(defrule drule
(drule (id ?id)
(name1 ?name1)
(name2 ?name2))
(claim (name ?name1))
(claim (name ?name2))
(claim (name ?name1 | ?name2)
(field $?pf1 'EmpName' $?)
(value $?pv1&:(= (length$ ?pf1) (length$ ?pv1)) ?name $?))
=>
(assert (success ?name)))
CLIPS>
(deffacts initial
(claim (name 'Employee')
(field 'Company')
(value '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')))
CLIPS> (reset)
CLIPS> (agenda)
0 drule: f-3,f-1,f-2,f-2
For a total of 1 activation.
CLIPS> (run)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (claim (name 'Employee') (field 'Company') (value 'ABC'))
f-2 (claim (name 'Event') (field 'EmpName' 'EventName' 'Company') (value 'Bob' 'Conference' 'ABC'))
f-3 (drule (name1 'Employee') (id '001') (field1 'Company') (value1 'ABC') (name2 'Event') (field2 'EventName') (value2 'Conference'))
f-4 (success 'Bob')
For a total of 5 facts.
CLIPS>

Related

clips how to make rule match forall but one

How could i make a rule that test if all facts from a deftemplate but one matches an specific condition?
Example: with
(deftemplate person (field name)(field hair-color))
having several blonde people, only one is not
get
(printout t "Only " ?name-not-blond " is not blonde" crlf)
CLIPS (6.4 2/9/21)
CLIPS>
(deftemplate person
(slot name)
(slot hair-color))
CLIPS>
(defrule only-one-not-blonde
(person (name ?name-not-blonde)
(hair-color ~blonde))
(not (person (name ~?name-not-blonde)
(hair-color ~blonde)))
=>
(println "Only " ?name-not-blonde " is not blonde."))
CLIPS>
(deffacts initial
(person (name Sue) (hair-color blonde))
(person (name Frank) (hair-color blonde))
(person (name Josh) (hair-color brown)))
CLIPS> (reset)
CLIPS> (facts)
f-1 (person (name Sue) (hair-color blonde))
f-2 (person (name Frank) (hair-color blonde))
f-3 (person (name Josh) (hair-color brown))
For a total of 3 facts.
CLIPS> (agenda)
0 only-one-not-blonde: f-3,*
For a total of 1 activation.
CLIPS> (assert (person (name Anne) (hair-color red)))
<Fact-4>
CLIPS> (agenda)
CLIPS>

Rule does not trigger even having the facts in WM

As the title suggests, I have a "generate-hotels" rule which, although being in the correct module and having all the facts that are needed in WM, this rule does not occur.
(defrule HOTELS::generate-hotels
(hotel (name ?name) (tr ?tr) (stars ?s) (price-per-night ?ppn))
(tourism-resort (name ?tr) (region ?r) (type $? ?t $?) (score ?s))
(tourism-type ?t)
(attribute (name best-region) (value ?r) (certainty ?certainty-1))
(attribute (name best-city) (value ?city&:(eq ?city (sym-cat (str-cat ?r "-") ?tr))) (certainty ?certainty-2))
(attribute (name best-tourism-type) (value ?t) (certainty ?certainty-3))
=>
(assert (attribute (name hotel) (value ?name)
(certainty (min ?certainty-1 ?certainty-2 ?certainty-3)))))

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:
Check whether all fields:value pair in drule are found in claim facts.
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
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.
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)))

CLIPS: check more than one fact

Let's consider the next trivial template:
(deftemplate person (ssn ?s))
I want to check that, if a person "is registered", there are no other person with same ssn, however, I've tried with something like:
(defrule repeated-person
(person (ssn ?s1))
(person (ssn ?s2))
(test (= ?s1 ?s2))
=>
(printout t "No, no, no..." clrf))
or even,
(defrule repeated-person
(person (ssn ?s))
(person (ssn ?s))
=>
(printout t "No, no, no..." clrf))
but it didn't work.
How can I accomplish something like that?
By default, you can't create duplicates of facts:
CLIPS (6.31 2/3/18)
CLIPS>
(deftemplate person
(slot SSN))
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-1>
CLIPS> (facts)
f-0 (initial-fact)
f-1 (person (SSN 123-45-6789))
For a total of 2 facts.
CLIPS> (assert (person (SSN 123-45-6789)))
FALSE
CLIPS> (facts)
f-0 (initial-fact)
f-1 (person (SSN 123-45-6789))
For a total of 2 facts.
CLIPS>
You can change this behavior using the set-fact-duplication function:
CLIPS> (set-fact-duplication TRUE)
FALSE
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-2>
CLIPS> (facts)
f-0 (initial-fact)
f-1 (person (SSN 123-45-6789))
f-2 (person (SSN 123-45-6789))
For a total of 3 facts.
CLIPS>
You can then write a rule which checks to see if there are two different facts with the same SSN:
CLIPS>
(defrule repeated-person
?f1 <- (person (SSN ?ss))
?f2 <- (person (SSN ?ss))
(test (< (fact-index ?f1) (fact-index ?f2)))
=>
(printout t "Duplicated SSN " ?ss crlf))
CLIPS> (agenda)
0 repeated-person: f-1,f-2
For a total of 1 activation.
CLIPS>
Since each fact has a unique fact index, the comparison in the test conditional element ensures that the facts matching the first and second patterns are not the same.
If we add another person with an identical SSN, we'll get multiple activations of the rule:
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-3>
CLIPS> (agenda)
0 repeated-person: f-1,f-3
0 repeated-person: f-2,f-3
0 repeated-person: f-1,f-2
For a total of 3 activations.
CLIPS>
We can dynamically assign a unique id to each created fact which allows to create "duplicate" facts even when facts duplication is disabled:
CLIPS> (clear)
CLIPS> (set-fact-duplication FALSE)
TRUE
CLIPS>
(deftemplate person
(slot id (default-dynamic (gensym*)))
(slot SSN))
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-1>
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-2>
CLIPS> (assert (person (SSN 123-45-6789)))
<Fact-3>
CLIPS> (facts)
f-0 (initial-fact)
f-1 (person (id gen1) (SSN 123-45-6789))
f-2 (person (id gen2) (SSN 123-45-6789))
f-3 (person (id gen3) (SSN 123-45-6789))
For a total of 4 facts.
CLIPS>
We can then create a rule which prints a single message regardless of the number of people with the same SSN:
CLIPS>
(defrule repeated-person
(person (id ?id) (SSN ?ssn))
(not (person (id ?id2&:(< (str-compare ?id2 ?id) 0)) (SSN ?ssn)))
(exists (person (id ~?id) (SSN ?ssn)))
=>
(printout t "Duplicated SSN " ?ssn crlf))
CLIPS> (agenda)
0 repeated-person: f-1,*,*
For a total of 1 activation.
CLIPS> (run)
Duplicated SSN 123-45-6789
CLIPS>

CLIPS counting facts or template instances that match the pattern

First I declare:
(deftemplate worker
(slot id
(type STRING)
(default ?DERIVE))
(slot salary
(type FLOAT)
(default ?DERIVE)))
then I add:
(assert(worker(id "a")(salary 30.0)))
(assert(worker(id "b")(salary 40.0)))
(assert(worker(id "c")(salary 60.0)))
(assert(worker(id "d")(salary 70.0)))
(assert(worker(id "e")(salary 10.0)))
How can I count how many 'workers' I have?
How can I count for example how many workers have salary over 30?
Use the fact-set query functions:
CLIPS>
(deftemplate worker
(slot id (type STRING) (default ?DERIVE))
(slot salary (type FLOAT) (default ?DERIVE)))
CLIPS> (assert (worker (id "a") (salary 30.0)))
<Fact-1>
CLIPS> (assert (worker (id "b") (salary 40.0)))
<Fact-2>
CLIPS> (assert (worker (id "c") (salary 60.0)))
<Fact-3>
CLIPS> (assert (worker (id "d") (salary 70.0)))
<Fact-4>
CLIPS> (assert (worker (id "e") (salary 10.0)))
<Fact-5>
CLIPS> (find-all-facts ((?f worker)) (> ?f:salary 30.0))
(<Fact-2> <Fact-3> <Fact-4>)
CLIPS> (length$ (find-all-facts ((?f worker)) (> ?f:salary 30.0)))
3
CLIPS> (do-for-all-facts ((?f worker)) (> ?f:salary 30.0) (printout t ?f:id crlf))
b
c
d
CLIPS>

Resources