Clips: comparing facts between each other - clips

I'm trying to create an expert system which each time the user responds a question it creates a new fact (for example):
assert(like accion yes)
assert(like multi yes)
And once its done, it shows the name of every game that has those facts in their description:
(deffacts gaming (game Call_of_Duty multi accion)
(game BattleField multi strategy))
I tried using this rule, where I check if every "like-fact" is located inside an especific "game-fact", but it doesn't work:
(defrule conclusion
(like $?x yes)
(game ?y $?x2)
(test(member$ ?x ?x2))
=>
(printout t "You like the game: " ?y crlf))

Here's three different ways you can write the rule. Your original rule, conclusion-1, will print a message for each like that is matched by a game so you can get multiple prints for each game. Rule conclusion-2 prints a game if there is at least one like that matches the game. At most you will see a game printed once. Rule conclusion-3 will print a game only if it matches every like for that game.
CLIPS>
(deffacts gaming
(like accion yes)
(like multi yes)
(game Call_of_Duty multi accion)
(game BattleField multi strategy))
CLIPS>
(defrule conclusion-1
(like $?x yes)
(game ?y $?x2)
(test(member$ ?x ?x2))
=>
(printout t "1 You like the game: " ?y crlf))
CLIPS>
(defrule conclusion-2
(game ?y $?x2)
(exists (like $?x yes)
(test (member$ ?x ?x2)))
=>
(printout t "2 You like the game: " ?y crlf))
CLIPS>
(defrule conclusion-3
(game ?y $?x2)
(forall (like $?x yes)
(test (member$ ?x ?x2)))
=>
(printout t "3 You like the game: " ?y crlf))
CLIPS> (reset)
CLIPS> (run)
1 You like the game: BattleField
2 You like the game: BattleField
1 You like the game: Call_of_Duty
1 You like the game: Call_of_Duty
2 You like the game: Call_of_Duty
3 You like the game: Call_of_Duty
CLIPS>

Related

CLIPS. How can I check if a list of facts matching facts already given?

I need to create a rule that will check if the list of facts I have entered matches the facts already given. Then the fact / facts corresponding to at least one of the entered ones are displayed.
this is what I have:
(deftemplate rule (multislot problem) (slot cause))
(deffacts info
(rule (problem one) (cause one1))
(rule (problem two) (cause two2))
(rule (problem three) (cause three3))
(defrule reading-input
=>
(printout t "Enter your problems: " )
(assert (problem (read))))
(defrule checking-input
(problem $?problem)
(rule (problem $?problem1) (cause ?cause1))
(test (eq ?problem ?problem1))
=>
(printout t "cause: " ?cause1 crlf))
how this should work:
CLIPS> Enter your problems: one two
CLIPS> cause: one1
cause: two2
Using the read function will retrieve just one value from your input. You need to use the readline function in conjunction with the explode$ function:
CLIPS (6.4 2/9/21)
CLIPS> (assert (problem (read)))
one two
<Fact-1>
CLIPS> (assert (problem (readline)))
one two
<Fact-2>
CLIPS> (assert (problem (explode$ (readline))))
one two
<Fact-3>
CLIPS> (facts)
f-1 (problem one)
f-2 (problem "one two")
f-3 (problem one two)
For a total of 3 facts.
CLIPS>
You can then use multifield wildcards to isolate individual problems within your rule:
CLIPS> (clear)
CLIPS>
(deftemplate rule
(multislot problem)
(slot cause))
CLIPS>
(deffacts info
(rule (problem one) (cause one1))
(rule (problem two four) (cause two2))
(rule (problem one three five) (cause three3)))
CLIPS>
(defrule reading-input
=>
(printout t "Enter your problems: " )
(assert (problem (explode$ (readline)))))
CLIPS>
(defrule checking-input
(problem $? ?problem $?)
(rule (problem $? ?problem $?) (cause ?cause))
=>
(printout t "Problem: " ?problem " cause: " ?cause crlf))
CLIPS> (reset)
CLIPS> (run)
Enter your problems: one two
Problem: one cause: three3
Problem: one cause: one1
Problem: two cause: two2
CLIPS>

How to automatically add many variables in a loop-for-count in CLIPS?

I want to use a loop-for-count to loop many different variables to let user input and let CLIPS read the variables.
For example:
Question: "How many dependent you wish to add?"
Answer: 5.
Then the CLIPS should create the variables like:
name1
name2
name3
name4
name5
Here is my code:
(printout t "How many dependent you wish to add? (Must have atleast 1): ")
(bind ?DepNo (read))
(assert (DepNo ?DepNo))
(loop-for-count (?DepNo 1 ?DepNo) do
(printout t "Name: ")
(bind $?DepName (explode$ (readline)))
(assert (DepName $?DepName))
)
Create a multifield value to hold all of the dependent names:
CLIPS (6.31 6/12/19)
CLIPS>
(defrule get-dependents
=>
(printout t "How many dependent you wish to add? (Must have at least 1): ")
(bind ?DepNo (read))
(bind ?depNames (create$))
(loop-for-count ?DepNo
(printout t "Name: ")
(bind ?depNames (create$ ?depNames (readline))))
(assert (DepNames ?depNames)))
CLIPS>
(defrule print-names
(DepNames $?depNames)
=>
(printout t "Dependents are: ")
(foreach ?d ?depNames
(printout t " " ?d crlf)))
CLIPS> (reset)
CLIPS> (run)
How many dependent you wish to add? (Must have at least 1): 3
Name: Sally Jones
Name: Fred Jones
Name: David Jones
Dependents are:
Sally Jones
Fred Jones
David Jones
CLIPS> (facts)
f-0 (initial-fact)
f-1 (DepNames "Sally Jones" "Fred Jones" "David Jones")
For a total of 2 facts.
CLIPS>

Is there a way to define CLIPS rules in order to search text?

I'm trying to define some rules with CLIPS for searching pieces of text in paragraphs or documents (e.g. filter the words that contain the letter 'a' or search for words that appear more than once), but I cannot find any example. Where can I find some examples for my problem?
Within your rule patterns you'd place constraints either on multiple words from your paragraph (such as to see if the same words appears more than once):
CLIPS>
(deftemplate paragraph
(multislot words))
CLIPS>
(defrule more-than-once
(paragraph (words $? ?w $? ?w $?))
=>
(assert (more-than-once ?w)))
CLIPS>
(defrule print-more-than-once
(more-than-once ?w)
=>
(printout t "'" ?w "' appears more than once." crlf))
CLIPS>
(assert (paragraph (words the quick brown fox jumped over the lazy dogs)))
<Fact-1>
CLIPS> (run)
'the' appears more than once.
CLIPS>
Or you'd place them on a single word:
CLIPS>
(defrule contains-e
(paragraph (words $? ?w&:(str-index "e" ?w) $?))
=>
(assert (contains ?w e)))
CLIPS>
(defrule print-contains
(contains ?w ?l)
=>
(printout t "'" ?l "' is contained in '" ?w "'." crlf))
CLIPS> (run)
'e' is contained in 'the'.
'e' is contained in 'jumped'.
'e' is contained in 'over'.
CLIPS>

Check multiple facts in CLIPS

Let's say I have some facts (I do not know how many there are) like this: lamp x is off. With a defrule I proggressively turn all lamps on so every fact will be: lamp x is on. How do I check every lamp that is on. I know that if there were three lamps I could write:
(defrule checkAllLamps
(lamp 1 is on)
(lamp 2 is on)
(lamp 3 is on)
=>
(printout t "All lamps are on now")
)
But for x lamps?
Thank you!
You can use fact-set query functions for that (chapter 12.9.12 of the Basic Programming Guide).
(deftemplate lamp
(slot id (type INTEGER))
(slot state (type SYMBOL)))
(defrule all-lamps-are-on
(lamp (state on))
(test (>= (length$ (find-all-facts ((?l lamp)) (eq ?l:state on))) 3))
=>
(printout t "All lamps are on" crlf))
Here's how you can check whether all of the lamps are on. The checkAllLamps rule treats the case where there are no lamps at all as all lamps being on, whereas the checkAllLampsAtLeastOne rule requires that there is at least one lamp that is on.
CLIPS (6.31 2/3/18)
CLIPS>
(defrule checkAllLamps
(not (lamp ? is off))
=>
(printout t "All lamps are on now" crlf))
CLIPS>
(defrule checkAllLampsAtLeastOne
(exists (lamp ? is on))
(not (lamp ? is off))
=>
(printout t "All lamps are on now" crlf))
CLIPS> (agenda)
0 checkAllLamps: *
For a total of 1 activation.
CLIPS> (assert (lamp 1 is on))
<Fact-1>
CLIPS> (agenda)
0 checkAllLampsAtLeastOne: *,*
0 checkAllLamps: *
For a total of 2 activations.
CLIPS> (assert (lamp 2 is off))
<Fact-2>
CLIPS> (agenda)
CLIPS> (retract 2)
CLIPS> (assert (lamp 2 is on))
<Fact-3>
CLIPS> (agenda)
0 checkAllLampsAtLeastOne: *,*
0 checkAllLamps: *
For a total of 2 activations.
CLIPS>

CLIPS: forcing a rule to re-evaluate the value of a global variable?

Is it possible to cause CLIPS to re-evaluate the value of a global variable in a defrule? I have this:
(defrule encourage "Do we have a GPA higher than 3.7?"
(test (> (gpa) 3.7))
=>
(printout t "Keep up the excellent work!" crlf))
gpa is function that calculates and returns a number based on two global variables (grade points and number of credits). I read somewhere that changes to global variables do not invoke pattern matching. How do I go about forcing this? I want to print that string every time I do (run) as long as the GPA is higher than 3.7.
Don't attempt to use global variables or function calls in this manner. First, global variables are specifically designed to not trigger pattern matching. Second, it would take a bit of magic for CLIPS to know when a function call needs to be reevaluated as there are any number of changes which could cause a function to return a different value, not just changes to globals. If you want a particular piece of information to trigger pattern matching, then stick it in a fact or instance. It will make your code easier to understand if you parameterize the function calls and bind the values to be used as arguments in the conditions of the rule.
CLIPS> (clear)
CLIPS>
(deffunction gpa (?grade-points ?number-of-credits)
(/ ?grade-points ?number-of-credits))
CLIPS>
(defrule encourage "Do we have a GPA higher than 3.7?"
(grade-points ?gp)
(number-of-credits ?noc)
(test (> (gpa ?gp ?noc) 3.7))
=>
(printout t "Keep up the excellent work!" crlf))
CLIPS> (assert (grade-points 35) (number-of-credits 10))
<Fact-2>
CLIPS> (agenda)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (grade-points 35)
f-2 (number-of-credits 10)
For a total of 3 facts.
CLIPS> (retract 1)
CLIPS> (assert (grade-points 38))
<Fact-3>
CLIPS> (agenda)
0 encourage: f-3,f-2
For a total of 1 activation.
CLIPS>
Alternately, you can use the fact query functions to iterate over a group of facts to dynamically compute the gpa based on facts rather than globals. Each time you modify one of these facts (add or remove), you can also assert a fact indicating the gpa needs to be rechecked to trigger the encourage rule.
CLIPS> (clear)
CLIPS>
(deftemplate grade
(slot class)
(slot grade-points)
(slot credits))
CLIPS>
(deffunction gpa ()
(bind ?grade-points 0)
(bind ?credits 0)
(do-for-all-facts ((?g grade)) TRUE
(bind ?grade-points (+ ?grade-points ?g:grade-points))
(bind ?credits (+ ?credits ?g:credits)))
(if (= ?credits 0)
then 0
else (/ ?grade-points ?credits)))
CLIPS>
(defrule encourage
?f <- (check-gpa)
=>
(retract ?f)
(if (> (gpa) 3.7)
then
(printout t "Keep up the excellent work!" crlf)))
CLIPS> (gpa)
0
CLIPS> (assert (check-gpa))
<Fact-1>
CLIPS> (run)
CLIPS> (assert (grade (class Algebra) (grade-points 12) (credits 3)))
<Fact-2>
CLIPS> (gpa)
4.0
CLIPS> (assert (check-gpa))
<Fact-3>
CLIPS> (run)
Keep up the excellent work!
CLIPS> (assert (grade (class History) (grade-points 6) (credits 2)))
<Fact-4>
CLIPS> (gpa)
3.6
CLIPS> (assert (check-gpa))
<Fact-5>
CLIPS> (run)
CLIPS> (assert (grade (class Science) (grade-points 12) (credits 3)))
<Fact-6>
CLIPS> (gpa)
3.75
CLIPS> (assert (check-gpa))
<Fact-7>
CLIPS> (run)
Keep up the excellent work!
CLIPS>

Resources