Search code examples
clipsexpert-system

Is there a function in CLIPS which checks if all/any/none of the elements in a multifields match a predicate?


I am aware of the member$ function in CLIPS. But its downside is that it only works on literal values as input. Does CLIPS have all/any/none functions where the elements are tested for a predicate? Something that would probably look like: (for-all <range-var> <source> <predicate>).

The problem I'm facing:

(deftemplate list (multislot vals))

(defrule first-high-val
    (list
        (vals
            $?b ;; all of these should be less than or equal to 10
            ?x  ;; first value to be greater than 10
            $?e
    )   )
    (test (for-all ?k $?b (<= ?k 10)))
=>
    (println ?x)
)

Simply doing:

(defrule first-high-val
    (list
        (vals
            $?b
            ?x & :(> ?x 10)
            $?e
    )   )
=>
    (println ?x)
)

is incorrect, because for (4 2 7 4 33 5 9 3 11 8 3 0) the rule would have an activation for 33 and for 11 respectively, while 11 is not the first number to be greater than 10.


Solution

  • There's a foreach function which allows you to apply actions to every member of a list, but there's not a comparable function for seeing if every member satisfies a query. You can write a deffunction that will do the iteration and predicate test for you:

    CLIPS> (deftemplate list (multislot vals))
    CLIPS> 
    (deffunction for-every (?list ?op ?value)
       (foreach ?item ?list
          (if (not (funcall ?op ?item ?value)) then (return FALSE)))
       (return TRUE))
    CLIPS> 
    (defrule first-high-val
       (list (vals $?b ?x&:(> ?x 10) $?e))
       (test (for-every ?b <= 10))
       =>
       (println ?x))
    CLIPS> (assert (list (vals 4 2 7 4 33 5 9 3 11 8 3 0)))
    <Fact-1>
    CLIPS> (run)
    33
    CLIPS>