I have a fact
(is-started TRUE)
I want to find if the fact exists with
(any-factp ((?is (is-started TRUE))) TRUE)
but this gives me Missing function declaration for is-started.
How can i determine if this fact exists with any-factp?
The fields of an implied deftemplate fact can be accessed using the implicitly defined multifield slot named implied:
CLIPS> (assert (is-started TRUE))
<Fact-1>
CLIPS> (any-factp ((?f is-started)) TRUE)
TRUE
CLIPS> (any-factp ((?f is-started)) (eq ?f:implied (create$ TRUE)))
TRUE
CLIPS> (any-factp ((?f is-started)) (eq ?f:implied (create$)))
FALSE
CLIPS>
Related
I know there are several predicate functions that allow to test if a variable is of a certain type.
For example,
(test (integerp ?my_var))
returns TRUE if my_var is of type integer.
Say I have a class A. How do I test if a variable is of type A? That is, I want to test if ?my_var is an instance of A.
I am looking for something like this:
(test (instance_of_A_p ?my_var))
or
(test (is-a (?my_var A))
Use the type function:
CLIPS> (bind ?a 3)
3
CLIPS> (type a)
SYMBOL
CLIPS> (defclass A (is-a USER))
CLIPS> (bind ?a (make-instance a1 of A))
[a1]
CLIPS> (type ?a)
A
CLIPS> (eq (type ?a) A)
TRUE
CLIPS>
So in the LHS of the rule:
(test (eq (type ?a) A))
I originally attempted using the modify function but it doesn't do anything and just prints false, I don't know what I am doing wrong.
I used
(modify ?tv (v ?x))
it didn't work.
I then used
(retract ?tv)
(assert (v ?x))
instead, which worked.
But I don't want to type that out every time I want to modify a fact, so I made a deffunction to do it for me, but
(deffunction modfact(?index ?factname ?factvalue)
(retract ?index)
(assert (?factname ?factvalue))
)
in this it gives a syntax error of:
[PRNTUTIL2] Syntax Error: Check appropriate syntax for first field of a RHS pattern.
ERROR:
(deffunction MAIN::modfact
(?index ?factname ?factvalue)
(retract ?index)
(assert (?factname
Which seems to me that its saying that I can't actually make this function because I can't assert a fact with the value of the variable. How can I get this to work?
Modify only works with facts that have an associated deftemplate defined with slots:
CLIPS>
(deftemplate task
(slot id)
(slot completed))
CLIPS> (watch facts)
CLIPS> (assert (task (id x) (completed no)))
==> f-1 (task (id x) (completed no))
<Fact-1>
CLIPS>
(defrule modit
?f <- (task (completed ~yes))
=>
(modify ?f (completed yes)))
CLIPS> (run)
<== f-1 (task (id x) (completed no))
==> f-2 (task (id x) (completed yes))
CLIPS>
When using the assert command, the first field of the fact must be a symbol. If you must get around this restriction you can use the str-assert function.
CLIPS>
(deffunction modfact (?index ?factname ?factvalue)
(retract ?index)
(str-assert (str-cat "(" ?factname " " ?factvalue ")")))
CLIPS> (assert (v 3))
==> f-3 (v 3)
<Fact-3>
CLIPS> (modfact 3 v 4)
<== f-3 (v 3)
==> f-4 (v 4)
<Fact-4>
CLIPS>
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>
In my system the user inputs a Y or N to answer simple questions. I call this rule after every question to increment a counter. There are some general problems with my code but i can't see where
(defrule QPain
(initial-fact)
=>
(printout t "Are You In Pain? " crlf)
(bind ?*Answer* (read))
)
(defrule IncSym
(test(=(str-compare (?*Answer*) "y")0))
=>
(bind ?*symcount* (+ ?*symcount* 1))
)
Thanks
The syntactic errors can be corrected as follows:
CLIPS> (clear)
CLIPS> (defglobal ?*Answer* = nil)
CLIPS> (defglobal ?*symcount* = 0)
CLIPS>
(defrule QPain
=>
(printout t "Are you in pain? ")
(bind ?*Answer* (read)))
CLIPS>
(defrule IncSym
(test (eq ?*Answer* y))
=>
(bind ?*symcount* (+ ?*symcount* 1)))
CLIPS> (reset)
CLIPS> (run)
Are you in pain? y
CLIPS> (show-defglobals)
?*Answer* = y
?*symcount* = 0
CLIPS>
This won't produce the behavior you're expecting, however, since ?*symcount* will not be incremented. The behavior of global variables and why you should not be using them in the manner you're attempting has been discussed previously:
How exactly (refresh) works in the clips?
CLIPS: forcing a rule to re-evaluate the value of a global variable?
Number equality test fails in CLIPS pattern matching?
CLIPS constant compiler directive
How can I run the clips with out reset the fact when using CLIPS
Instead of using global variables to track responses and symptoms, you should use facts or instances. Here's one approach:
CLIPS> (clear)
CLIPS>
(deftemplate symptom
(slot id)
(slot response))
CLIPS>
(deftemplate symptom-list
(multislot values))
CLIPS>
(deffacts initial
(symptom-list))
CLIPS>
(defrule QPain
=>
(printout t "Are you in pain? ")
(assert (symptom (id in-pain) (response (read)))))
CLIPS>
(defrule IncSym
(symptom (id ?id) (response y))
?f <- (symptom-list (values $?list))
(test (not (member$ ?id ?list)))
=>
(modify ?f (values ?list ?id)))
CLIPS>
(defrule symptoms-found
(declare (salience -10))
(symptom-list (values $?list))
=>
(printout t "Symptom count: " (length$ ?list) crlf))
CLIPS> (reset)
CLIPS> (run)
Are you in pain? y
Symptom count: 1
CLIPS> (reset)
CLIPS> (run)
Are you in pain? n
Symptom count: 0
CLIPS>
And another:
CLIPS> (clear)
CLIPS>
(deftemplate symptom
(slot id)
(slot response))
CLIPS>
(defrule QPain
=>
(printout t "Are you in pain? ")
(assert (symptom (id in-pain) (response (read)))))
CLIPS>
(defrule symptoms-found
(declare (salience -10))
=>
(bind ?count (find-all-facts ((?f symptom)) (eq ?f:response y)))
(printout t "Symptom count: " (length$ ?count) crlf))
CLIPS> (reset)
CLIPS> (run)
Are you in pain? y
Symptom count: 1
CLIPS> (reset)
CLIPS> (run)
Are you in pain? n
Symptom count: 0
CLIPS>
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>