How can I find the ID of the variable that has the maximum value in CLIPS? - max

I tried to find the best student according to their exam marks. I took them from the user. I want CLIPS to give me the ID of the best student. For example, student1 mark 70, student2 mark 80 and student 3 mark 100. CLIPS should tell me "The best student is ... because his/her point is ..." I used global variables but I'm not sure if it's true because it doesn't work.
(defglobal ?*student1mark* = 0)
(defglobal ?*student2mark* = 0)
(defglobal ?*student3mark* = 0)
(defrule get-marks
=>
(printout t "What is the exam mark of student1?" crlf)
(bind ?*student1mark* (read))
(assert (stu1mark ?*student1mark*))
(printout t "What is the exam mark of student2?" crlf)
(bind ?*student2mark* (read))
(assert (stu2mark ?*student2mark*))
(printout t "What is the exam mark of student3?" crlf)
(bind ?*student3mark* (read))
(assert (stu3mark ?*student3mark*))
(build (str-cat
"(deffacts students (student student1 " ?*student1mark* " student student2 " ?*student2mark* " student student3 " ?*student3mark* "))")))
(defrule whichstudent
(student ?ID = (max ?*student1mark*" ?*student2mark*" ?*student3mark*))
=>
(printout t "The best student is " ?ID crlf))

I would not use global variables. I would go with a template and facts.
One solution with the help of a rule would be this:
CLIPS (6.30 3/17/15)
CLIPS> (deftemplate student
(slot id (type INTEGER) (default ?NONE))
(slot mark (type INTEGER) (default ?NONE))
)
CLIPS> (deffacts students
(student (id 1) (mark 80))
(student (id 2) (mark 79))
(student (id 4) (mark 60))
(student (id 3) (mark 90))
)
CLIPS> (defrule best-mark
(compare-students)
(student (id ?id) (mark ?mark))
(not
(student (id ?) (mark ?nmark&:(> ?nmark ?mark)))
)
=>
(printout t "The best student is student no. " ?id crlf)
)
CLIPS> (reset)
CLIPS> (assert (compare-students))
<Fact-5>
CLIPS> (run)
The best student is student no. 3
The key part is
(student (id ?id) (mark ?mark))
(not
(student (id ?) (mark ?nmark&:(> ?nmark ?mark)))
)
So this rule matches with a student fact, if there is no other student with a higher mark.

Related

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>

How can I get the sum of items in a multislot

I have a template like the one shown bellow. How can I get the sum of items in the multislot grades?
(deftemplate student
(multislot name)
(multislot grades)
)
Here's one way to do it. In the reg6 rule, the + function is given two arguments of 0 in addition to the grades to insure that the + function allows has at least 2 arguments; otherwise, if there were zero or one grades for the student you'd get an error.
CLIPS (6.31 2/3/18)
CLIPS>
(deftemplate student
(multislot name)
(multislot grades))
CLIPS>
(deftemplate sum
(multislot name)
(slot grade))
CLIPS>
(defrule reg6
(student (name $?name)
(grades $?grades))
=>
(assert (sum (name ?name)
(grade (+ 0 0 (expand$ ?grades))))))
CLIPS>
(assert (student (name David Green) (grades))
(student (name Sue Brown) (grades 90))
(student (name Frank Black) (grades 85 75)))
<Fact-3>
CLIPS> (run)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (student (name David Green) (grades))
f-2 (student (name Sue Brown) (grades 90))
f-3 (student (name Frank Black) (grades 85 75))
f-4 (sum (name Frank Black) (grade 160))
f-5 (sum (name Sue Brown) (grade 90))
f-6 (sum (name David Green) (grade 0))
For a total of 7 facts.
CLIPS>
You can use the expand$ function. Check in the Basic Programming Guide the Multifield Expansion Function chapter to know more.
(deftemplate student
(multislot name)
(multislot grades))
(defrule grades-sum
(student (grades $?grades))
=>
(printout t "Student grades sum is " (+ (expand$ ?grades))))
(assert (student (grades (create$ 1 2 3 4 5))))
(student (name) (grades 1 2 3 4 5))
(run)
Student grades sum is 15

how to get the objects in clips in order on LHS side based on a particular slot in class

Is there any way to get the objects in clips in order on LHS side based on a particular slot in class?
(defclass SAMPLE
"all the information about students"
(is-a BASE_SAMPLE) (role concrete) (pattern-match reactive)
(slot ID (create-accessor read-write) (access initialize-only) (propagation inherit) (visibility public) (type INTEGER))
(slot NAME (create-accessor read-write) (access initialize-only) (propagation inherit) (visibility public) (type STRING))
)
if I have 100 SAMPLE objects, and I want all of them to come in ascending order based on the slot ID on the LHS of a rule, is this poosilbe in clips?
There's two ways you can sort the objects. You can do it on the LHS by adding some additional information to either the objects or a separate fact/instance to retain information on which objects have been processed:
CLIPS> (clear)
CLIPS>
(defclass STUDENT
(is-a USER)
(slot id)
(slot full-name)
(slot processed (default no)))
CLIPS>
(definstances people
(of STUDENT (id 102) (full-name "Fred Jones"))
(of STUDENT (id 438) (full-name "Sally Smith"))
(of STUDENT (id 391) (full-name "John Farmer")))
CLIPS>
(defrule list
?i <- (object (is-a STUDENT)
(id ?id1)
(processed no))
(not (object (is-a STUDENT)
(id ?id2&:(> ?id1 ?id2))
(processed no)))
=>
(modify-instance ?i (processed yes))
(printout t ?id1 " " (send ?i get-full-name) crlf))
CLIPS> (reset)
CLIPS> (run)
102 Fred Jones
391 John Farmer
438 Sally Smith
CLIPS>
Or you can sort the values on the RHS:
CLIPS> (clear)
CLIPS>
(defclass STUDENT
(is-a USER)
(slot id)
(slot full-name))
CLIPS>
(definstances students
(of STUDENT (id 102) (full-name "Fred Jones"))
(of STUDENT (id 438) (full-name "Sally Smith"))
(of STUDENT (id 391) (full-name "John Farmer")))
CLIPS>
(deffunction id-sort (?i1 ?i2)
(> (send ?i1 get-id) (send ?i2 get-id)))
CLIPS>
(defrule list
=>
(bind ?instances (find-all-instances ((?i STUDENT)) TRUE))
(bind ?instances (sort id-sort ?instances))
(progn$ (?i ?instances)
(printout t (send ?i get-id) " " (send ?i get-full-name) crlf)))
CLIPS> (reset)
CLIPS> (run)
102 Fred Jones
391 John Farmer
438 Sally Smith
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>

Create rules in CLIPS to move from unknown to known person facts

Assume that you are given a set of 'person' facts that are defined according to the following construct:
(deftemplate person (slot name) (slot sex) (allowed-values male female) (multislot children))
Write rules to do the following:
Create a fact of the form (unknown-person ) for each name that appears in the children multislot of a person fact but not in the name slot of any person fact (it is assumed that no two people have the same name)
For each fact of the form (unknown-person ) ask the user for the sex of the person, retract the fact and assert a new fact of the form (unknown-person ).
For each fact of the form (unknown-person ), retract the fact and create a new person fact for the person (it is assumed that the person has no children).
Your rules should do data validation to ensure that only an allowed value for is supplied by the user
Define the template in CLIPS:
(deftemplate person
(slot name)
(slot sex)
(slot gender (allowed-values male female))
(multislot children))
Start with the unknown-person creation (caveat: this may not be completely correct as it still creates a person without checking to see if they exist).
(defrule childrencataloguer "First layer of unknown person resolution"
(person (children $?ch))
=>
(progn$ (?term ?ch)
(assert (unknown-person ?term))
))
Deal with the caveat above
(defrule removeunknownswithpersonsalready
(person (name ?n))
?up <-(unknown-person ?n)
=>
(retract ?up))
Now, get the gender:
(defrule getgender
?up-nogen <-(unknown-person ?n)
=>
(retract ?up-nogen)
(printout t crlf "Please enter male or female to indicate " ?n "'s gender" crlf )
(assert (unknown-person ?n (read)))
)
There are other ways you can do the gender confirmation, I would have liked to use the deftemplate itself, so that the allowed-values would have fed into the validation. But I don't know how yet.
(assert (gender male))
(assert (gender female))
Now, do validation:
(defrule checkgender
?p <- (unknown-person ?n ?s)
(not (gender ?s))
=>
(retract ?p)
(assert (unknown-person ?n))
)
Finally, graduate from unknown
(defrule graduatefromunknown
(declare (salience -10))
?up <- (unknown-person ?n ?s)
=>
(retract ?up)
(assert (person (name ?n) (sex ?s)))
)

Resources