clips how to make rule match forall but one - clips

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>

Related

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: match when value not in multislot

I am trying to write a rule that will match when a certain value is not in a multislot, and then add that value to it.
(deftemplate person
(multislot packing_list
(type SYMBOL)
(default ?DERIVE)))
(defrule apply_adapter
(travel international)
?p <- (person (packing_list $? ~travel_adaptor ))
=>
(modify ?p (packing_list travel_adaptor)))
(println "Added to list" crlf)
)
(deffacts start
(travel international)
(person)
)
Two parts of this I know aren't correct:
?p <- (person (packing_list $? ~travel_adaptor )) does not fire the rule - what is the correct syntax?
(modify ?p (packing_list travel_adaptor))) probably does not do what I want, which is to insert the value, not replace the list.
Any ideas how to fix this?
CLIPS (Cypher Beta 8/21/18)
CLIPS>
(deftemplate person
(multislot packing_list
(type SYMBOL)
(default ?DERIVE)))
CLIPS>
(defrule apply_adapter
(travel international)
?p <- (person (packing_list $?pl))
(test (not (member$ travel_adaptor ?pl)))
=>
(modify ?p (packing_list ?pl travel_adaptor))
(println "Added to list"))
CLIPS>
(deffacts start
(travel international)
(person))
CLIPS> (reset)
CLIPS> (run)
Added to list
CLIPS> (facts)
f-1 (travel international)
f-2 (person (packing_list travel_adaptor))
For a total of 2 facts.
CLIPS>

Cannot assert the same fact in the memory

In clips I have the following fact in the memory. and I want to regenerate it by some rule. When I regenerate it it does not add it to the facts. because all fields value are same. Any solution to this problem?
(objct (name food) (edible? yes) (isa Object))
Basic Programming Guide, section 13.4.4, Setting the Duplication Behavior of Facts. http://clipsrules.sourceforge.net/documentation/v630/bpg.pdf
CLIPS> (clear)
CLIPS> (deftemplate objct (slot name) (slot edible?) (slot isa))
CLIPS> (assert (objct (name food) (edible? yes) (isa Object)))
<Fact-1>
CLIPS> (assert (objct (name food) (edible? yes) (isa Object)))
FALSE
CLIPS> (facts)
f-0 (initial-fact)
f-1 (objct (name food) (edible? yes) (isa Object))
For a total of 2 facts.
CLIPS> (set-fact-duplication TRUE)
FALSE
CLIPS> (assert (objct (name food) (edible? yes) (isa Object)))
<Fact-2>
CLIPS> (facts)
f-0 (initial-fact)
f-1 (objct (name food) (edible? yes) (isa Object))
f-2 (objct (name food) (edible? yes) (isa Object))
For a total of 3 facts.
CLIPS>

how to get the index of facts in RHS of rule?

I'm asking if there is a possibility of accessing a get the index of fact in RHS of defrule ?
It gives me that undefined every time I try to index a fact in a RHS of defrule.
because I have a while loop , I want to be able to modify elevator fact depending on my input data.
(deftemplate elevator
(slot goal))
(deffacts elevator
(elevator (goal 0)))
(defrule read-data
=>
?f1 <- (elevator)
(modify ?f2 (goal 1))
)
this an example of my code , since I can't put all online :
(deftemplate data
(slot data)
)
(deffacts data
(data (data 1))
)
(defrule rule1
?f1 <-(data)
=>
(bind ?value (readline input) )
(while (neq ?value EOF)
do
(bind ?data (fact-slot-value ?f1 data))
(printout t "data " ?data crlf )
(retract ?f1)
(modify ?f1 (data ?value))
(bind ?value (readline input)
)
)
)
this is my input file :
2
3
4
5
6
7
this is what I'm getting :
CLIPS> (run)
data 1
data FALSE
data FALSE
data FALSE
data FALSE
data FALSE
CLIPS>
I want it to print out
data 2
data 3
data 4 ..ect
You can do it this way from the RHS of a rule, but if your rule actually has no LHS conditions, it's pointless to use a rule to change the value of the fact. Just use a function.
CLIPS>
(deftemplate elevator
(slot goal))
CLIPS>
(deffacts elevator
(elevator (goal 0)))
CLIPS>
(defrule read-data
=>
(do-for-fact ((?f elevator)) TRUE
(modify ?f (goal 1))))
CLIPS> (watch facts)
CLIPS> (reset)
<== f-0 (initial-fact)
==> f-0 (initial-fact)
==> f-1 (elevator (goal 0))
CLIPS> (run)
<== f-1 (elevator (goal 0))
==> f-2 (elevator (goal 1))
CLIPS>
Alternately, you can bind the fact you want to modify in the conditions of the rule:
CLIPS> (clear)
CLIPS>
(deftemplate elevator
(slot goal))
CLIPS>
(deffacts elevator
(elevator (goal 0)))
CLIPS>
(defrule read-data
?f <- (elevator (goal 0))
=>
(modify ?f (goal 1)))
CLIPS> (watch facts)
CLIPS> (reset)
<== f-0 (initial-fact)
==> f-0 (initial-fact)
==> f-1 (elevator (goal 0))
CLIPS> (run)
<== f-1 (elevator (goal 0))
==> f-2 (elevator (goal 1))
CLIPS>
Updated:
You can get your original rule to "work" by removing the retract and rebinding ?f1 to the value returned by modify:
CLIPS> (clear)
CLIPS>
(deftemplate data
(slot data))
CLIPS>
(deffacts data
(data (data 1)))
CLIPS>
(defrule rule1
?f1 <- (data)
=>
(bind ?value (readline input))
(while (neq ?value EOF)
(bind ?data (fact-slot-value ?f1 data))
(printout t "data " ?data crlf )
(bind ?f1 (modify ?f1 (data ?value)))
(bind ?value (readline input))))
CLIPS> (reset)
CLIPS> (open input.txt input)
TRUE
CLIPS> (run)
data 1
data 2
data 3
data 4
data 5
data 6
CLIPS> (close input)
TRUE
CLIPS>
It's still suspiciously complicated to be modifying the same fact multiple times on the RHS.

Obtain ID of fact I just asserted

Is there a way for me to get the fact ID of a fact that I just asserted in the RHS of a rule? Something along the lines of
?f <- (assert (new-fact))
CLIPS>
(defrule example
=>
(bind ?f (assert (new-fact)))
(bind ?i (fact-index ?f))
(printout t "The fact index is " ?i crlf))
CLIPS> (reset)
CLIPS> (run)
The fact index is 1
CLIPS> (facts)
f-0 (initial-fact)
f-1 (new-fact)
For a total of 2 facts.
CLIPS>

Resources