Can't reference inherited slot in a CLIPS message handler - clips

I have an abstract class with one defined read-only slot named storage-size:
(defclass digital-media (is-a USER)
(role abstract)
(slot storage-size
(type INTEGER)
(default -1)
(access read-only)
(visibility public)))
I've attached a primary message handler to my abstract class which references the same storage-size slot.
(defmessage-handler digital-media read-storage primary (?offset ?size)
(if (or (< ?offset 0) (>= ?offset ?self:storage-size))
then
(printout t "Read offset " ?offset " is out of bounds, max storage size is " ?self:storage-size crlf)
(halt))
(printout t "Still running for some reason..." crlf))
When making a concrete class, the inherited field works as expected only if it's implicitly declared:
(defclass compact-disk (is-a digital-media)
(role concrete))
(make-instance my-disk of compact-disk)
(send [my-disk] print)
; [my-disk] of compact-disk
; (storage-size -1)
(send [my-disk] read-storage 128 1024)
; Read offset 128 is out of bounds, max storage size is -1
However, when I provide a default value for it the message handler doesn't work:
(defclass compact-disk (is-a digital-media)
(role concrete)
(slot storage-size
(source composite)
(default 650)))
(make-instance my-disk of compact-disk)
(send [my-disk] print)
; [my-disk] of compact-disk
; (storage-size 650)
(send [my-disk] read-storage 128 1024)
; [MSGPASS3] Static reference to slot storage-size of class digital-media does not apply to [my-disk] of compact-disk
; [ARGACCES5] Function >= expected argument #2 to be of type integer or float
; [PRCCODE4] Execution halted during the actions of message-handler read-storage primary in class digital-media
; FALSE

I don't fully understand the rationale, but when you change the slot facets for a subclass, the superclass message-handlers can't use slot shorthand references for that slot when called by an instance of the subclass. So in the read-storage handler you'll need to change ?self:storage-size to (send ?self get-storage-size).
I suppose this enforces encapsulation of the superclass. For example, the read-storage message-handler knows the ?self:storage-size reference must be an integer and that it's not possible for a subclass to redefine the type of this slot to a string and then invoke this message-handler with a subclass instance containing a string rather than an integer value for the slot.

Related

Test if a fact with a field value of a multifield exists in Clips

I have the following fact templates for a station and a train:
(deftemplate station
(slot name (type SYMBOL))
(slot status (type SYMBOL) (default open)))
(deftemplate train
(slot departs-from (type SYMBOL))
(multislot stops-at (type SYMBOL)))
Both slots departs-from and stops-at reference names of stations.
(deffacts trains-and-stations
(station (name station-a))
(station (name station-b))
(station (name station-c))
(train (departs-from station-a) (stops-at unknown-station))
(train (departs-from station-a) (stops-at station-b station-c))
(train (departs-from station-b) (stops-at station-c station-a)))
I want to make a rule that tests if a station fact used in each train exists:
(defrule check-valid-stations
?train <- (train (departs-from ?from) (stops-at $?stops))
(not (exists (station (name ?from))))
=>
(printout t "Route of " $train " contains an unknown station!" crlf))
The rule above only checks if a departure station is valid, how do I check if both departure station and every stop station exist?
The simplest thing to do is have separate rules that check the departs-from and stops-at stations and fire each time an unknown station is found. If you want a single message to be printed regardless of the number of unknown stations, then add an id slot to each train fact so that you can write more complex patterns that will match just once for each train.
CLIPS (6.31 6/12/19)
CLIPS>
(deftemplate station
(slot name (type SYMBOL))
(slot status (type SYMBOL) (default open)))
CLIPS>
(deftemplate train
(slot id)
(slot departs-from (type SYMBOL))
(multislot stops-at (type SYMBOL)))
CLIPS>
(deffacts trains-and-stations
(station (name station-a))
(station (name station-b))
(station (name station-c))
(train (id 1) (departs-from station-a) (stops-at unknown-station))
(train (id 2) (departs-from station-a) (stops-at station-b station-c))
(train (id 3) (departs-from station-b) (stops-at station-c station-a))
(train (id 4) (departs-from some-station) (stops-at station-c station-a bad-location)))
CLIPS>
(defrule check-valid-stations-departs
?train <- (train (departs-from ?from))
(not (station (name ?from)))
=>
(printout t "Route of " ?train " contains an unknown station: " ?from crlf))
CLIPS>
(defrule check-valid-stations-stops
?train <- (train (stops-at $? ?to $?))
(not (station (name ?to)))
=>
(printout t "Route of " ?train " contains an unknown station: " ?to crlf))
CLIPS>
(defrule check-valid-stations
?train <- (train (id ?id) (departs-from ?from))
(exists (or (not (station (name ?from)))
(and (train (id ?id) (stops-at $? ?to $?))
(not (station (name ?to))))))
=>
(printout t "Route of " ?train " contains an unknown station!" crlf))
CLIPS> (reset)
CLIPS> (run)
Route of <Fact-7> contains an unknown station: some-station
Route of <Fact-7> contains an unknown station!
Route of <Fact-7> contains an unknown station: bad-location
Route of <Fact-4> contains an unknown station!
Route of <Fact-4> contains an unknown station: unknown-station
CLIPS>

Clips rule for an object going into infinite loop after modifying the content inside it

I am incrementing the variable inside an clips object by some number on RHS.
The rule is working, but going into infinite loop.
I tried running it without modifying the variable inside the object, it is coming into RHS once, but with modification it is going into loop.
(defrule modify
"modify"
(step 0)
?EA <- (object (is-a ALERT)
(ID ?RID&:(or(eq ?ID "R") (eq ?RID "Q")))
(TIME ?T)
)
=>
(bind ?time (send ?EA get-TIME))
(bind ?newTime (+ 86399 ?time))
(send ?EA put-TIME ?newTime)
(log_info (str-cat "old time is " ?time ", new time is " ?newTime "event time is " (send ?EA get-TIME)))
)
I am expecting the log to be printed once even after modifying the content inside object.
Thanks.
Removing the Slot on the LHS which we are incrementing on RHS fixed this.
(defrule modify
"modify"
(step 0)
?EA <- (object (is-a ALERT)
(ID ?RID&:(or(eq ?ID "R") (eq ?RID "Q")))
)
=>
(bind ?time (send ?EA get-TIME))
(bind ?newTime (+ 86399 ?time))
(send ?EA put-TIME ?newTime)
(log_info (str-cat "old time is " ?time ", new time is " ?newTime "event time is " (send ?EA get-TIME)))
)
If we keep slot TIME in LHS, after the change/increment the rule is getting re-triggered, and it is going in loop.

Checking a object property in LHS

I need to check the existence of a value for an o'bject's property in a LHS.
(defrule check-property
?room <- (object (is-a ROOM))
(integerp (send ?room get-property)) ; #1
=>
(printout ?*debug-print* "Room " ?room " has property" crlf))
But it seems to me that #1 is not valuated in LHS. Instead if I put it in RHS, it returns TRUE.
Where am I wrong?
Thx,
Nic
Use the test conditional element to evaluate an expression in the LHS of a rule:
(defrule check-property
?room <- (object (is-a ROOM))
(test (integerp (send ?room get-property)))
=>
(printout ?*debug-print* "Room " ?room " has property" crlf))
It's better to explicitly retrieve the slot value by matching it rather than using the slot accessor as this will cause the condition to be reevaluated whenever the slot value changes:
(defrule check-property
?room <- (object (is-a ROOM)
(property ?property))
(test (integerp ?property))
=>
(printout ?*debug-print* "Room " ?room " has property" crlf))

can i retrieve fact's value after searching for it? Clips

I know how to search to check whether fact slot exists, I need to know how to obtain the value of other slots in that fact
(deftemplate details
(slot name(type STRING))(slot occ (type STRING))......)
and
(deffacts details
(details (occ "student")(name "mike"))
(details (occ "professor")(name "john"))
.......
)
(defrule search
(userinfo(occ?occ))
(details(occ?occ) )
=>
?????????????????????????
)
Just bind them in the conditions of the rule:
(defrule search
(userinfo (occ ?occ))
(details (occ ?occ) (name ?name))
=>
(printout t "Name is " ?name crlf))

How to make CLIPS program work?

I am new to CLIPS and I thought of looking over existing solved problems for a start and try to figure it backwards. There is the following problem found in Giarratano-Riley: Expert Systems and Programming 3rd edition and it states the following:
Fires are classified according to the principal burning material.
Translate the following information to rules for determining fire
class.
Type A fires involve ordinary combustibles such as paper, wood, and cloth.
Type B fires involve flammable and combustible liquids (such as
oil and gas), greases, and similar materials.
Type C fires involve energized electrical equipment.
Type D fires involve combustible metals such as magnesium, sodium, and potassium.
The type of extinguisher that should be used on a fire depends on the fire class.
Translate the following information to rules.
Class A fires should be extinguished with heat-absorbing or
combustion-retarding extinguishers such as water or water-based
liquids and dry chemicals.
Class B fires should be extinguisher by mexcluding air, inhibiting the release of
combustible vapors, or interrupting the combustion chain reaction. Extinguishers
include dry chemicals, carbon dioxide, foam, and bromotrifluoromethane.
Class C fires should be extinguished with a non conducting agent to prevent
short circuits. If possible the power should be cut. Extinguishers
include dry chemicals, carbon dioxide, and bromotrifluoromethane.
Class D fires should be extinguished with smothering and heat- absorbing chemicals > that do not react with the burning metals. Such chemicals include trimethoxyboroxine
and screened graphitized coke.
Describe the facts used in the rules. The input to the program should be made by asserting the type of burning material as a fact. The output should indicate which extinguishers may be used and other actions that should be taken, such as cutting off the power. Show that your program works for one material form each of the fire classes.
And then it is solved by Berkely and the code is the following. My question is, how do I call these rules and make the program work? I loaded the buffer, reset, run and it only loads the rules into the command-line.
; Define templates used in rules
(deftemplate fire (slot burning-material))
(deftemplate extinguisher-system (multislot function) (multislot extinguisher))
(deftemplate response (multislot actions))
(deftemplate fire-class (slot class))
; Define rules for determining fire classes
(defrule class-A-fire
(fire (burning-material paper | wood | cloth | other-ordinary-combustibles)) =>
(assert (fire-class (class A))))
(defrule class-B-fire
(fire (burning-material oil | gas | greases | other-flammable-combustible-liquids)) =>
(assert (fire-class (class B))))
(defrule class-C-fire
(fire (burning-material energized-electrical-equipment)) =>
(assert (fire-class (class C))))
(defrule class-D-fire
(fire (burning-material magnesium | sodium | potassium | other-combustible-metals)) =>
(assert (fire-class (class D))))
; Define rules for determining the type of extinguisher that should be used on a fire
(defrule class-A-emergency
(fire-class (class A))
=>
(assert (response (actions activate-extinguisher-A)))
(assert (extinguisher-system (function heat-absorbing combustion-retarding) (extinguisher water water-based-liquids dry-chemicals))))
(defrule class-B-emergency
(fire-class (class B))
=>
(assert (response (actions activate-extinguisher-B)))
(assert (extinguisher-system (function excluding-air inhibiting-release-of-combustible-vapors interrupting-combustion-chain-reaction) (extinguisher dry-chemicals carbon-dioxide foam bromotrifluoromethane))))
(defrule class-C-emergency
(fire-class (class C))
=>
(assert (response (actions activate-extinguisher-C power-cut)))
(assert (extinguisher-system (function nonconducting-agent) (extinguisher dry-chemicals carbon-dioxide bromotrifluoromethoane))))
(defrule class-D-emergency
(fire-class (class D))
=>
(assert (response (actions activate-extinguisher-D)))
(assert (extinguisher-system (function smothering-heatabsorbing-chemicals) (extinguisher trimethoxyboroxine screened-graphitized-coke))))
I guess you made it until here:
CLIPS> Loading Selection...
Defining deftemplate: fire
Defining deftemplate: extinguisher-system
Defining deftemplate: response
Defining deftemplate: fire-class
Defining defrule: class-A-fire +j+j
Defining defrule: class-B-fire +j+j
Defining defrule: class-C-fire +j+j
Defining defrule: class-D-fire +j+j
Defining defrule: class-A-emergency +j+j
Defining defrule: class-B-emergency +j+j
Defining defrule: class-C-emergency +j+j
Defining defrule: class-D-emergency +j+j
CLIPS> (reset)
Now you need to load the problem data. For example, for a wood fire:
CLIPS> (assert (fire (burning-material wood)))
<Fact-1>
CLIPS> (facts)
f-0 (initial-fact)
f-1 (fire (burning-material wood))
And the, run the rules engine
CLIPS> (run)
CLIPS> (facts)
f-0 (initial-fact)
f-1 (fire (burning-material wood))
f-2 (fire-class (class A))
f-3 (response (actions activate-extinguisher-A))
f-4 (extinguisher-system (function heat-absorbing combustion-retarding) (extinguisher water water-based-liquids dry-chemicals))
And clean it to check the next problem
CLIPS> (reset)
CLIPS> (assert (fire (burning-material gas)))
<Fact-1>
CLIPS> (run)
...

Resources