I have the following CLIPS construct below and I'm interested in tallying up how many times a particular rule fired so the output will be a series of the rules fired followed by their count (see picture for example).
So it would be all those rules followed by:
temperature: (times fired) seconds
pressure: (times fired) seconds
(deftemplate oil-measure
(slot utc-time (type STRING))
(slot temperature (type INTEGER))
(slot pressure (type INTEGER)))
(defrule oil-is-hot
(oil-measure (temperature ?temp) (utc-time ?time))
(test (> ?temp 32))
=>
(printout t ?time tab "temperature:" tab ?temp crlf))
(defrule pressure-is-high
(oil-measure (pressure ?press&:(> ?press 0)) (utc-time ?time))
=>
(printout t ?time tab "pressure:" tab ?press crlf))
You can store counters in global variables and increase them accordingly.
(defglobal ?*hot-oil-times* = 0)
(defrule oil-is-hot
(oil-measure (temperature ?temp) (utc-time ?time))
(test (> ?temp 32))
=>
(bind ?*hot-oil-times* (+ ?*hot-oil-times* 1))
(printout t ?time tab "temperature:" tab ?*hot-oil-times* tab ?temp crlf))
Code incorporating noxdafox's solution:
(defglobal ?*hot-oil-times* = 0)
(defglobal ?*high-pressure-times* = 0)
(deftemplate oil-measure
(slot utc-time (type STRING))
(slot temperature (type INTEGER))
(slot pressure (type INTEGER)))
(defrule oil-is-hot
(oil-measure (temperature ?temp) (utc-time ?time))
(test (> ?temp 32))
=>
(bind ?*hot-oil-times* (+ ?*hot-oil-times* 1))
(printout t ?time tab "temperature:" tab ?*hot-oil-times* tab ?temp crlf))
(defrule pressure-is-high
(oil-measure (pressure ?press&:(> ?press 0)) (utc-time ?time))
=>
(bind ?*high-pressure-times* (+ ?*high-pressure-times* 1))
(printout t ?time tab "pressure:" tab ?*high-pressure-times* tab ?press crlf))
Related
I'm a beginner to CLIPS. The goal of this program is to return the name of the company that best matches the user's demographics. I ask the user their gender and (might be storing it wrong) store it into input, ethnicity stored into input2, and age into input3. I'm running into errors when trying to do comparisons to return the company name. Eg. if input=1 (man), then compare the demographics of men in both companies and return the name of the company with more men.
(= 1 input) this line in the last rule gives me error "Function '=' expected argument #2 to be of type integer or float." which I thought I've already only allowed responses of integers. Any help would be appreciated, thank you!
(deftemplate company
(slot name)
(slot men)
(slot women)
(slot Asian)
(slot Black)
(slot Latinx)
(slot Indigenous)
(slot White)
(slot Other)
(slot <18)
(slot 18-20)
(slot 21-29)
(slot 30-39)
(slot 40+))
(deffacts demographics
(company (name Google)(men 0.669)(women 0.331)(Asian 0.428)(Black 0.088)(Latinx 0.088)(Indigenous 0.007)(White 0.445)(Other 0)(<18 0.02)(18-20 0.11)(21-29 0.59)(30-39 0.19)(40+ 0.07))
(company (name Apple)(men 0.652)(women 0.348)(Asian 0.279)(Black 0.094)(Latinx 0.148)(Indigenous 0.007)(White 0.438)(Other 0.032)(<18 0.01)(18-20 0.1)(21-29 0.57)(30-39 0.22)(40+ 0.06)))
(defrule begin => (assert (phase select-gender)))
(defrule menu (phase select-gender) =>
(printout t "Do you identify as a (1) man or (2) woman?: ") (assert (userinput (read))))
(defrule selection-okay
?phase <- (phase select-gender)
?input <- (userinput ?select&1|2)
=>
(retract ?phase)
(assert (selection ?select))
(assert (phase select-ethnicity)))
(defrule selection-nokay
?phase <- (phase select-gender)
?input <- (userinput ?select&~1&~2)
=>
(retract ?phase)
(assert (phase select-gender))
(printout t ?select " is not a valid response. Please enter 1 or 2."crlf))
(defrule menu2 (phase select-ethnicity) =>
(printout t "Do you identify as (1)Asian, (2)Black, (3)Latinx, (4)Indigenous, (5)White, or (6)Other?: ") (assert (userinput2 (read))))
(defrule selection-okay2
?phase <- (phase select-ethnicity)
?input2 <- (userinput2 ?select&1|2|3|4|5|6)
=>
(retract ?phase)
(assert (selection ?select))
(assert (phase select-age)))
(defrule selection-nokay2
?phase <- (phase select-ethnicity)
?input2 <- (userinput2 ?select&~1&~2&~3&~4&~5&~6)
=>
(retract ?phase)
(assert (phase select-ethnicity))
(printout t ?select " is not a valid response. Please enter 1 through 6."crlf))
(defrule menu3 (phase select-age) =>
(printout t "You are age: (1)<18, (2)18-20, (3)20-30, (4)30-40, (5)40+?: ") (assert (userinput3 (read))))
(defrule selection-okay3
?phase <- (phase select-age)
?input3 <- (userinput3 ?select&1|2|3|4|5)
=>
(retract ?phase)
(assert (selection ?select))
(assert (phase company-gender-men)))
(defrule selection-nokay3
?phase <- (phase select-age)
?input3 <- (userinput3 ?select&~1&~2&~3&~4&~5)
=>
(retract ?phase)
(assert (phase select-age))
(printout t ?select " is not a valid response. Please enter 1 through 5."crlf))
(defrule get-company-by-gender-men
?phase <- (company-gender-men)
(= 1 input)
(> (fact-slot-value 1 men) (fact-slot-value 2 men))
=>
(retract ?phase)
(printout t (fact-slot-value 1 name) crlf))
;(fact-slot-value 1 name)
;(> (fact-slot-value 1 men) (fact-slot-value 2 men))
I have tried removing some extra code just to see if I can access the previous user input but I'm still not getting an output.
(deftemplate company
(slot name)
(slot men)
(slot women)
(slot Asian)
(slot Black)
(slot Latinx)
(slot Indigenous)
(slot White)
(slot Other)
(slot <18)
(slot 18-20)
(slot 21-29)
(slot 30-39)
(slot 40+))
(deffacts demographics
(company (name Google)(men 0.669)(women 0.331)(Asian 0.428)(Black 0.088)(Latinx 0.088)(Indigenous 0.007)(White 0.445)(Other 0)(<18 0.02)(18-20 0.11)(21-29 0.59)(30-39 0.19)(40+ 0.07))
(company (name Apple)(men 0.652)(women 0.348)(Asian 0.279)(Black 0.094)(Latinx 0.148)(Indigenous 0.007)(White 0.438)(Other 0.032)(<18 0.01)(18-20 0.1)(21-29 0.57)(30-39 0.22)(40+ 0.06)))
(defrule begin => (assert (phase select-gender)))
(defrule menu (phase select-gender) =>
(printout t "Do you identify as a (1) man or (2) woman?: ") (assert (userinput (read)))(assert(phase select-ethnicity)))
(defrule menu2 (phase select-ethnicity) =>
(printout t "Do you identify as (1)Asian, (2)Black, (3)Latinx, (4)Indigenous, (5)White, or (6)Other?: ") (assert (userinput2 (read)))(assert(phase select-age)))
(defrule menu3 (phase select-age) =>
(printout t "You are age: (1)<18, (2)18-20, (3)20-30, (4)30-40, (5)40+?: ") (assert (userinput3 (read)))(assert(phase company-gender-men)))
(defrule get-company-by-gender-men
?phase <- (company-gender-men)
(userinput ?userinput)
=>
(retract ?phase)
(printout t ?userinput "can see user input"crlf))
(defrule get-company-by-gender-men
?phase <- (phase company-gender-men)
(userinput 1)
(company (name ?name) (men ?men1))
(company (men ?men2))
(test (> ?men1 ?men2))
=>
(retract ?phase)
(printout t ?name crlf))
I'm trying to increment a defglobal variable (symcount) by 1 if the user defines that they have pain by using the (read) function
(defrule QPain
(initial-fact)
=>
(printout t "Are You In Pain? " crlf)
(bind (ans Answer) (read))
)
(defrule AnsInc
(Answ Answer = "y")
=>
(bind ?*symcount* (+ ?*symcount* 1)))
the increment must only happen of the user presses "y"
otherwise the increment must not happen.
CLIPS> (defglobal ?*symcount* = 0)
CLIPS>
(defrule QPain
=>
(printout t "Are You In Pain? ")
(bind ?answer (read))
(if (eq ?answer y)
then
(bind ?*symcount* (+ ?*symcount* 1))))
CLIPS> (reset)
CLIPS> (run)
Are You In Pain? y
CLIPS> ?*symcount*
1
CLIPS> (reset)
CLIPS> (run)
Are You In Pain? n
CLIPS> ?*symcount*
0
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>
i have deftemplate for url that contains url itself and integer counter :
(deftemplate url_t
(slot counter
(type INTEGER)
(default 0))
(slot url
(type STRING)
(default "")))
and i am trying to define the rule to print url string if counter value >0
(defrule print-url
(url_t (counter ?counter) (url ?url))
(test (> ?counter 0))
=>
(printout t "url is " ?url crlf)
)
But when i try to change the values of url_t:
(assert url_t (counter 2) (url "hello hello"))
The rule is not executed. Where's my error?
If the assert statement in your description is exactly what you entered, then that's your problem because the syntax is invalid. Other than that, there's nothing wrong with the syntax of your deftemplate or defrule:
CLIPS> (clear)
CLIPS> (unwatch all)
CLIPS>
(deftemplate url_t
(slot counter
(type INTEGER)
(default 0))
(slot url
(type STRING)
(default "")))
CLIPS>
(defrule print-url
(url_t (counter ?counter) (url ?url))
(test (> ?counter 0))
=>
(printout t "url is " ?url crlf))
CLIPS> (assert (url_t (counter 2) (url "hello hello")))
<Fact-1>
CLIPS> (facts)
f-0 (initial-fact)
f-1 (url_t (counter 2) (url "hello hello"))
For a total of 2 facts.
CLIPS> (agenda)
0 print-url: f-1
For a total of 1 activation.
CLIPS> (run)
url is hello hello
CLIPS> (modify 1 (counter 3) (url "goodbye goodbye"))
<Fact-2>
CLIPS> (agenda)
0 print-url: f-2
For a total of 1 activation.
CLIPS> (run 1)
url is goodbye goodbye
CLIPS> (modify 2 (counter 0) (url "won't fire"))
<Fact-3>
CLIPS> (agenda)
CLIPS>
Here is my situation:
I want to run the CLIPS periodically and the system can record how many times it runs.
for example: I type in the terminal "run" many times to call the system periodically. then the system can record how many the system runs and show it on the screen. Here is my .clp file
(defglobal ?*lock* = 0)
(deftemplate counter
(slot number))
(deffacts initial_data
(counter (number 0))
)
(defrule set_counter
?f<-(counter (number ?x))
(test (= ?*lock* 0))
=>
(bind ?*lock* 1)
(printout t "plus 1" crlf)
(modify ?f (number (+ ?x 1)))
)
(defrule show_result
?f<-(counter (number ?x))
(test (= ?*lock* 1))
=>
(printout t "the counter value has been changed:" crlf)
(ppfact (fact-index ?f) t)
(bind ?*lock* 0)
)
I use a global value as a lock to control the rules and store the running times in the fact named counter. Now is my problem: Once the system finishes running for the first time. There are no rules in the agenda any more. I want the system can run again with out resetting the facts and the system can process the facts saved form the first running process. How can I refresh the agenda or rematch the rule without reset the facts?
I have find some commands like (refresh rule-name) and (refresh-agenda) but they can not solve my problem. I just type in "(refresh set_counter)" and "(refresh-agenda)" after "(run)". However, no rules are added into agenda.
I don't know if there are solution to my problem or clips can not work like this?
Another question is I try (refresh rule-name) with
(defglobal ?*lock* = 0)
(deftemplate counter
(slot number))
(deftemplate set
(slot number))
(deffacts initial_data
(counter (number 0))
)
(defrule set_counter
(initial-fact)
=>
(bind ?*lock* (+ ?*lock* 1))
(printout t ?*lock* crlf)
)
It works fine. I don't know why it doesn't work in the first example?
really thanks for any advice.
Global variables don't trigger pattern matching so you shouldn't use them in the condition of a rule unless the value is never changed. If you use a fact to represent the lock, the rules will execute for as many cycles as you specify:
(deftemplate lock
(slot value))
(deftemplate counter
(slot number))
(deffacts initial_data
(counter (number 0))
(lock (value 0))
)
(defrule set_counter
?f <- (counter (number ?x))
?l <- (lock (value 0))
=>
(modify ?l (value 1))
(printout t "plus 1" crlf)
(modify ?f (number (+ ?x 1)))
)
(defrule show_result
?f <- (counter (number ?x))
?l <- (lock (value 1))
=>
(printout t "the counter value has been changed:" crlf)
(ppfact (fact-index ?f) t)
(modify ?l (value 0))
)