Search code examples
clipsexpert-system

CLIPS: One-time pattern matching a value of a multislot


I read in this SO answer that

When an instance is created or deleted, all patterns applicable to that object are updated. However, when a slot is changed, only those patterns that explicitly match on that slot are affected.

Now I have the following problem: I have a multislot that receives more and more items over time.

I have a rule R1 that fires if "some_value" is contained in the multislot. When I add "some_value" to the multislot everything works as expected. However, if I add another item, say "another_value" to the multislot, R1 fires again. This is consistent to what I cited above, but it is not what I want. I want R1 to fire only once if "some_value" is contained in the multislot, and I don't want R1 to fire again if another value is added to the multislot.

How can I do that?

I could use many slots instead of a multislot, but this would not work in case I don't know the number of possible values.


Solution

  • If you can't assign the values to different slots--which is how you'd normally handle firing rules on selected changes--then you need to track which changes have been processed. Tracking either the rules or values that have been processed would be the most straightforward way to do this. If each rule processes just one value, then tracking the rules would be better so that you could have multiple rules triggered for the same value change. Here's an example where rules R1 and R2 are restricted to a single change and rule R3 exhibits the behavior you're currently experiencing:

    CLIPS> (clear)
    CLIPS> 
    (defclass XAMPL
       (is-a USER)
       (multislot properties)
       (multislot processed))
    CLIPS>    
    (definstances initial
      ([x1] of XAMPL))
    CLIPS> 
    (defrule add_some_value
       (declare (salience -1))
       ?o <- (object (name [x1])
                     (properties $?p&:(not (member$ some_value ?p))))
       =>
       (modify-instance ?o (properties some_value ?p)))  
    CLIPS> 
    (defrule add_another_value
       (declare (salience -2))
       ?o <- (object (name [x1])
                     (properties $?p&:(not (member$ another_value ?p))))
       =>
       (modify-instance ?o (properties another_value ?p)))  
    CLIPS>      
    (defrule R1
       ?o <- (object (name [x1])
                     (properties $?properties&:(member$ some_value ?properties))
                     (processed $?processed&:(not (member$ R1 ?processed))))
       =>
       (modify-instance ?o (processed ?processed R1))
       (printout t "Rule R1 fires" crlf))  
    CLIPS> 
    (defrule R2
       ?o <- (object (name [x1])
                     (properties $?properties&:(member$ some_value ?properties))
                     (processed $?processed&:(not (member$ some_value ?processed))))
       =>
       (modify-instance ?o (processed ?processed some_value))
       (printout t "Rule R2 fires" crlf))  
    CLIPS> 
    (defrule R3
       (object (name [x1])
               (properties $?properties&:(member$ some_value ?properties)))
       =>
       (printout t "Rule R3 fires" crlf))  
    CLIPS> (reset)
    CLIPS> (run)
    Rule R2 fires
    Rule R1 fires
    Rule R3 fires
    Rule R3 fires
    CLIPS>