Search code examples
clips

How to implement collections (e.g. list) in CLIPS and how to interrogate such a collection


I am using fuzzyCLIPS 6.31 and I want to have a fact that is a collection (of other facts). Currently, I have something like this:

(deftemplate person "attributes of a person"
   (multislot name (type STRING) )
   (slot age (type FLOAT) (range 0.0 129.9) (default 0) )
   (slot gender (type SYMBOL) (allowed-symbols male female) )
   ; .. other attributes
)

(def template people "collection of people"
    ; terminating char separated string of string representation of person
    ; e.g. "John Frederick Handel, 42.5, male, ....;Homer Jay Simpson, 45.2, male, ..."
    (slot members (type STRING) )
)

(defrule make-person-and-add-to-group
    ; make a person fact
    ; amend previously asserted people fact by adding "stringified" person to people fact
)

(defrule predict-a-riot 
    ; IF we have (fuzzy) "too" many males in a group AND
    ; the males are above the age of 'X' AND
    ; some other salient facts
    ; => THEN
    ; assert riot prediction with CF
)

This could be an example of a simplistic expert system that attempt to predict the likelihood of a riot breaking out, based on some simplistic input variables and heuristics.

I have the following questions:

  1. Is "stringifying" facts (concatenating and then parsing/groking the resulting string) a good/acceptable way of dealing with lists (or collections) of facts ?
  2. How does one access and manipulate the string held in a slot of a fact?

Solution

  • Create a unique id for each person fact and store that in your collection rather than the content of the fact.

             CLIPS (6.31 6/12/19)
    CLIPS> 
    (deftemplate person
       (slot id (type SYMBOL))
       (slot name (type STRING))
       (slot age (type INTEGER) (range 0 130) (default 0))
       (slot gender (type SYMBOL) (allowed-symbols male female)))
    CLIPS> 
    (deftemplate people 
       (multislot members (type SYMBOL)))
    CLIPS>    
    (deffacts initial
       (people)
       (add-person))
    CLIPS> 
    (deffunction add-person ()
       (printout t "Name: ")
       (bind ?name (readline))
       (printout t "Age: ")
       (bind ?age (read))
       (printout t "Gender: ")
       (bind ?gender (read))
       (bind ?id (gensym*))
       (assert (person (id ?id)
                       (name ?name)
                       (age ?age)
                       (gender ?gender)))
       (return ?id))
    CLIPS>                    
    (defrule make-person-and-add-to-group
       ?p <- (people (members $?people))
       ?a <- (add-person)
       =>
       (retract ?a)
       (printout t "Add Person? ")
       (bind ?response (lowcase (read)))
       (if (or (eq ?response y) (eq ?response yes))
          then
          (bind ?id (add-person))
          (modify ?p (members ?id ?people))
          (assert (add-person))))
    CLIPS> (reset)
    CLIPS> (run)
    Add Person? yes
    Name: Fred Smith
    Age: 38
    Gender: male
    Add Person? yes
    Name: Sally Jones
    Age: 23
    Gender: female
    Add Person? no
    CLIPS> (facts)
    f-0     (initial-fact)
    f-3     (person (id gen1) (name "Fred Smith") (age 38) (gender male))
    f-6     (person (id gen2) (name "Sally Jones") (age 23) (gender female))
    f-7     (people (members gen2 gen1))
    For a total of 4 facts.
    CLIPS> 
    

    The general pattern then for doing something to each member of the collection is:

    (defrule for-each-person
       (people (members $? ?id $?))
       (person (id ?id)
               (name ?name)
               (age ?age)
               (gender ?gender))
       =>
       ;; Action
       )