Search code examples
clips

CLIPS beginner: How CLIPS rules interpret the facts order?


I would like to know in each day which activities I do, so I constructed the following code:

(deftemplate schedule
(slot activity)
(slot starthour)
(slot endhour)
)

(defrule r1
(schedule (activity ?a) (starthour ?start) (endhour ?end))
(not (busy ?start ?a))
=>
(assert (busy ?start ?a))
)

(defrule r2
(busy ?d ?a)
(schedule (activity ?a) (starthour ?start) (endhour ?end))
(test (< ?d ?end))
=>
(assert (busy ( + ?d 1) ?a))
)
CLIPS> (assert (schedule (activity reading) (starthour 3) (endhour 5)))
<Fact-1>
CLIPS> (assert (schedule (activity music) (starthour 4) (endhour 7)))
<Fact-2>

For the facts that I inserted I obtained a result that does not order the days. How CLIPS interpret the order of the facts? Is there a way to me to combine in a day more than 1 activity?

Thanks very much!


Solution

  • By default CLIPS uses a depth first strategy for determining which rule to execute next, so generally the next rule executed will have been activated by the last fact asserted or retracted. Section 5.3, Conflict Resolution Strategies, in the Basic Programming Guide describes this process in greater detail.

    As long as the correct facts are being generated by the rules, you shouldn't be particularly concerned with the order in which they're place on the fact-list because 1) It's not practical to use the fact-list to display the output of a program and 2) forcing a particular order of placement can be difficult and overly complex.

    Instead, to order facts in your program output, collect all of the relevant facts using one of the fact query functions and then use the sort function with a custom comparator to order the facts before printing them:

             CLIPS (6.4 2/9/21)
    CLIPS> 
    (deftemplate schedule
       (slot activity)
       (slot starthour)
       (slot endhour))
    CLIPS>    
    (deftemplate busy
       (slot activity)
       (slot hour))
    CLIPS> 
    (defrule r1
       (schedule (activity ?a) (starthour ?start) (endhour ?end))
       (not (busy (activity ?a) (hour ?start)))
       =>
       (assert (busy (activity ?a) (hour ?start))))
    CLIPS> 
    (defrule r2
       (busy (activity ?a) (hour ?d))
       (schedule (activity ?a) (starthour ?start) (endhour ?end))
       (test (< ?d ?end)) 
       =>
       (assert (busy (activity ?a) (hour (+ ?d 1)))))
    CLIPS>    
    (deffacts schedules
       (schedule (activity reading) (starthour 3) (endhour 5))
       (schedule (activity music) (starthour 4) (endhour 7)))
    CLIPS>    
    (deffunction busy-compare (?f1 ?f2)
        ;; Sort by hour
        (if (> (fact-slot-value ?f2 hour) (fact-slot-value ?f1 hour))
           then (return FALSE))
        (if (< (fact-slot-value ?f2 hour) (fact-slot-value ?f1 hour))
           then (return TRUE))
        ;; And then sort by activity
        (if (> (str-compare (fact-slot-value ?f2 activity) 
                            (fact-slot-value ?f1 activity)) 0)
           then (return FALSE)
           else (return TRUE)))
    CLIPS>            
     (defrule print
        (declare (salience -10))
        =>
        (bind ?schedule (find-all-facts ((?f busy)) TRUE))
        (bind ?schedule (sort busy-compare ?schedule))
        (foreach ?s ?schedule
           (format t "%2d %s%n" (fact-slot-value ?s hour) (fact-slot-value ?s activity))))
    CLIPS> (reset)
    CLIPS> (run)
     3 reading
     4 music
     4 reading
     5 music
     5 reading
     6 music
     7 music
    CLIPS> (facts)
    f-1     (schedule (activity reading) (starthour 3) (endhour 5))
    f-2     (schedule (activity music) (starthour 4) (endhour 7))
    f-3     (busy (activity music) (hour 4))
    f-4     (busy (activity music) (hour 5))
    f-5     (busy (activity music) (hour 6))
    f-6     (busy (activity music) (hour 7))
    f-7     (busy (activity reading) (hour 3))
    f-8     (busy (activity reading) (hour 4))
    f-9     (busy (activity reading) (hour 5))
    For a total of 9 facts.
    CLIPS> 
    

    Here's another way to do it storing multiple activities in each busy fact:

    CLIPS> (clear)
    CLIPS> 
    (deftemplate schedule
       (slot activity)
       (slot starthour)
       (slot endhour))
    CLIPS> 
    (deftemplate busy
       (multislot activity)
       (slot hour))
    CLIPS> 
    (defrule r1
       (schedule (activity ?a) (starthour ?start) (endhour ?end))
       =>
       (loop-for-count (?hour ?start ?end)
          (assert (busy (activity ?a) (hour ?hour)))))
    CLIPS> 
    (defrule combine
       ?b1 <- (busy (activity $?a) (hour ?d))
       ?b2 <- (busy (activity ?n&:(not (member$ ?n ?a))) (hour ?d))
       =>
       (modify ?b1 (activity ?a ?n))
       (retract ?b2))
    CLIPS> 
    (deffacts schedules
       (schedule (activity reading) (starthour 3) (endhour 5))
       (schedule (activity music) (starthour 4) (endhour 7)))
    CLIPS> 
    (deffunction busy-compare (?f1 ?f2)
        (if (> (fact-slot-value ?f2 hour) (fact-slot-value ?f1 hour))
           then (return FALSE))
        (if (< (fact-slot-value ?f2 hour) (fact-slot-value ?f1 hour))
           then (return TRUE))
        (return FALSE))
    CLIPS>     
     (defrule print
        (declare (salience -10))
        =>
        (bind ?schedule (find-all-facts ((?f busy)) TRUE))
        (bind ?schedule (sort busy-compare ?schedule))
        (foreach ?s ?schedule
           (format t "%2d %s%n" (fact-slot-value ?s hour) (implode$ (fact-slot-value ?s activity)))))
    CLIPS> (reset)
    CLIPS> (run)
     3 reading
     4 reading music
     5 reading music
     6 music
     7 music
    CLIPS> (facts)
    f-1     (schedule (activity reading) (starthour 3) (endhour 5))
    f-2     (schedule (activity music) (starthour 4) (endhour 7))
    f-5     (busy (activity music) (hour 6))
    f-6     (busy (activity music) (hour 7))
    f-7     (busy (activity reading) (hour 3))
    f-8     (busy (activity reading music) (hour 4))
    f-9     (busy (activity reading music) (hour 5))
    For a total of 7 facts.
    CLIPS>