Search code examples
clips

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?


Solution

  • 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>