CLIPS gives a floating point number upto many decimal places.
e.g
CLIPS> ( / 4 3)
1.33333333333333
How do I get a value rounded off to two decimal places (1.33 in this case)
CLIPS (6.31 6/12/19)
CLIPS> (bind ?n (/ 4 3))
1.33333333333333
CLIPS> (/ (integer (* ?n 100)) 100)
1.33
CLIPS>
(deffunction precision (?num ?digits)
(bind ?m (integer (** 10 ?digits)))
(/ (integer (* ?num ?m)) ?m))
CLIPS> (precision ?n 2)
1.33
CLIPS> (precision ?n 4)
1.3333
CLIPS> (precision ?n 0)
1.0
CLIPS>
Related
I want to build an expert system in which in a case of emergency at a building with some floors (it needs to work for any amount of floors) the elevator should take the people into the ground.
The thing is, that the defrule to send the elevator at any floor never makes it in the agenda, so the system just does nothing. The correct action should be to fire the rule and then another rule that takes the people from the floor.
The code for the defrule is this:
(defrule move_to_floor "elevator moves to any floor "
?i <- (elevator is_at floor ?x has ?y adults and ?z minors)
(floor ?fl&~?x has ?n adult and ?m minor people)
(test (> (+ ?n ?m) 0))
=>
(retract ?i)
(assert (elevator is_at floor ?fl has ?y adults and ?z minors))
)
The facts as they have been initialized from the user in another defrule above are these:
f-0 (initial-fact)
f-1 (elevator is_at 0 has 0 adults and 0 minors)
f-3 (capacity 4)
f-4 (floors 3)
f-5 (initCanEnter 0) ;At 0 this prevents from entering the init_defrule again
f-6 (floor 3 has 2 adult and 1 minor people)
f-7 (floor 2 has 4 adult and 5 minor people)
f-8 (floor 1 has 1 adult and 2 minor people)
I can't seem to find the solution. Also, I'm using deffacts and not deftemplate as I have seen many people using on the internet.
You can use the matches command to see which patterns in a rule are matched.
CLIPS (6.31 2/3/18)
CLIPS>
(defrule move_to_floor "elevator moves to any floor "
?i <- (elevator is_at floor ?x has ?y adults and ?z minors)
(floor ?fl&~?x has ?n adult and ?m minor people)
(test (> (+ ?n ?m) 0))
=>
(retract ?i)
(assert (elevator is_at floor ?fl has ?y adults and ?z minors)))
CLIPS>
(deffacts initial
(elevator is_at 0 has 0 adults and 0 minors)
(capacity 4)
(floors 3)
(initCanEnter 0) ;At 0 this prevents from entering the init_defrule again
(floor 3 has 2 adult and 1 minor people)
(floor 2 has 4 adult and 5 minor people)
(floor 1 has 1 adult and 2 minor people))
CLIPS> (reset)
CLIPS> (matches move_to_floor)
Matches for Pattern 1
None
Matches for Pattern 2
f-5
f-6
f-7
Partial matches for CEs 1 - 2
None
Activations
None
(3 0 0)
CLIPS>
In this case, the first pattern is not matched. That's because your pattern expects is_at floor ?x but your fact contains is_at 0 (the symbol floor is missing in your fact). If you correct this issue, the rule will be placed on the agenda.
CLIPS>
(deffacts initial
(elevator is_at floor 0 has 0 adults and 0 minors)
(capacity 4)
(floors 3)
(initCanEnter 0) ;At 0 this prevents from entering the init_defrule again
(floor 3 has 2 adult and 1 minor people)
(floor 2 has 4 adult and 5 minor people)
(floor 1 has 1 adult and 2 minor people))
CLIPS> (reset)
CLIPS> (agenda)
0 move_to_floor: f-1,f-7
0 move_to_floor: f-1,f-6
0 move_to_floor: f-1,f-5
For a total of 3 activations.
CLIPS>
If you issue a (run) command at this point, the rules will endlessly fire in a loop moving from floor to floor, so that's something you'll need to address next.
If you use deftemplate facts rather than ordered facts, you'll get an error if you misspell slot names, so it's better to use these if you have a fact with multiple attributes.
CLIPS> (clear)
CLIPS>
(deftemplate elevator
(slot at_floor (type INTEGER))
(slot adults (type INTEGER))
(slot minors (type INTEGER)))
CLIPS>
(deftemplate floor
(slot # (type INTEGER))
(slot adults (type INTEGER))
(slot minors (type INTEGER)))
CLIPS>
(deffacts initial
(elevator (at_floor 0))
(capacity 4)
(floors 3)
(initCanEnter 0)
(floor (# 3) (adults 2) (minors 1))
(floor (# 2) (adults 4) (minors 5))
(floor (# 1) (adults 1) (minors 2)))
CLIPS>
(defrule move_to_floor
?i <- (elevator (at_floor ?x))
(floor (# ?fl&~?x) (adults ?n) (minors ?m))
(test (> (+ ?n ?m) 0))
=>
(modify ?i (at_floor ?fl)))
CLIPS> (reset)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (elevator (at_floor 0) (adults 0) (minors 0))
f-2 (capacity 4)
f-3 (floors 3)
f-4 (initCanEnter 0)
f-5 (floor (# 3) (adults 2) (minors 1))
f-6 (floor (# 2) (adults 4) (minors 5))
f-7 (floor (# 1) (adults 1) (minors 2))
For a total of 8 facts.
CLIPS> (agenda)
0 move_to_floor: f-1,f-7
0 move_to_floor: f-1,f-6
0 move_to_floor: f-1,f-5
For a total of 3 activations.
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>
If I have a bunch of facts like (example (fact 1)), (example (fact 2)), (example (fact 3)), and have another list of facts like (myfact (number 2)), how can I perform a printout on each item in the first list that is not in the second (based on the number in the fact/number slots)? I suspect I need do-for-all-facts, but I'm not sure exactly how. Here's my incomplete code:
(deffunction difference ()
(do-for-all-facts ((?f1 example)) TRUE
(find-all-facts ((?f2 myfact)) (eq 1 1))
(if (somehow check if ?f1:fact does not equal ANY of number slots in ?f2) then
(printout t "..." crlf))))
CLIPS> (clear)
CLIPS> (deftemplate example (slot fact))
CLIPS> (deftemplate myfact (slot number))
CLIPS>
(deffacts start
(example (fact 1))
(example (fact 2))
(example (fact 3))
(myfact (number 2))
(myfact (number 4)))
CLIPS>
(deffunction difference ()
(do-for-all-facts ((?f1 example))
(not (any-factp ((?f2 myfact)) (eq ?f1:fact ?f2:number)))
(printout t "difference " ?f1:fact crlf)))
CLIPS> (reset)
CLIPS> (difference)
difference 1
difference 3
CLIPS>
(defrule difference
(example (fact ?n))
(not (myfact (number ?n)))
=>
(printout t "difference " ?n crlf))
CLIPS> (run)
difference 3
difference 1
CLIPS>
I want to write a rule that says the following
if x > y => assert x
where x and y are variables and their values are given as facts.
How do I do it?
If x already exists as a fact, then asserting it again from the actions of the rule would be unnecessary, but if you want to assert a fact indicating that x is greater than y then you could do it this way:
CLIPS>
(defrule greater-than
(x ?x)
(y ?y)
(test (> ?x ?y))
=>
(assert (x-is-greater-than-y)))
CLIPS> (assert (x 4))
<Fact-1>
CLIPS> (assert (y 1))
<Fact-2>
CLIPS> (agenda)
0 greater-than: f-1,f-2
For a total of 1 activation.
CLIPS> (run)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (x 4)
f-2 (y 1)
f-3 (x-is-greater-than-y)
For a total of 4 facts.
CLIPS>
I am trying to return a message if the user types in a value within certain range. Is this possible on CLIPS? In addition the system should only accept values in increments of 10.
If the user types in a number less or equal to 10 it should say "A"
If the user types in a number greater than 10 and less than 40 it should say "B"
- so it should only accept values 10,20,30,40
This is the code I have so far:
(defrule b-a1
(b-a "a")
=>
(bind ?reply (get-text-from-user "How many points did you achieve?"))
(assert (b-a1 ?reply )))
(defrule b-a2
(b-a1 <= 10)
=>
(assert (conclusion "A")))
(defrule b-a2
(10 < b-a1 < 40)
=>
(assert (conclusion "B")))
Any ideas on how I can get this working?
CLIPS>
(defrule b-a1
(b-a "a")
=>
(printout t "How many points did you achieve? ")
(bind ?reply (read))
(assert (b-a1 ?reply )))
CLIPS>
(defrule b-a2
(b-a1 ?v&:(<= ?v 10))
=>
(assert (conclusion "A")))
CLIPS>
(defrule b-a2
(b-a1 ?v&:(< 10 ?v)&:(< ?v 40))
=>
(assert (conclusion "B")))
CLIPS> (assert (b-a "a"))
<Fact-1>
CLIPS> (run)
How many points did you achieve? 24
CLIPS> (facts)
f-0 (initial-fact)
f-1 (b-a "a")
f-2 (b-a1 24)
f-3 (conclusion "B")
For a total of 4 facts.
CLIPS>