Counting is a non-monotonic operation, since the value of the counter is replaced by a new value. This makes it slightly awkward in Algernon, which is primarily oriented toward monotonic inference. However, by carefully using the non-monotonic :clear-slot form, we can implement a counter.
(tell '((:taxonomy (Things (Counters))))) (tell '((:slot increment (Counters Booleans) :cardinality 1) (:slot current-count (Counters :number) :cardinality 1))) (tell '((:rules Counters ((increment ?ctr true) (current-count ?ctr ?n) (:bind ?m (+ ?n 1)) -> (:clear-slot ?ctr current-count) (:clear-slot ?ctr increment) (current-count ?ctr ?m)))))
The forward-chaining rule to increment a counter c is invoked by asserting (increment c true). It binds a variable to the current count and computes the next value. In the consequent of the rule, it clears the count and asserts the next value. It also clears the predicate (increment c true) so that the next assertion of that value will invoke the forward-chaining rule again.
Testing it with the following assertion,
(tell '((:a ?c (isa ?c Counters)) (current-count ?c 0) (current-count ?c ?n1) (:eval (format t "~%Current counter value is ~a." ?n1)) (increment ?c true) (current-count ?c ?n2) (:eval (format t "~%Current counter value is ~a." ?n2)) (increment ?c true) (current-count ?c ?n) (:eval (format t "~%Current counter value is ~a." ?n)))
gives the following output.
ASSERTING: Create and test a counter Current counter value is 0. Current counter value is 1. Current counter value is 2. Result: Bindings: ?n --- 2 ?n2 --- 1 ?n1 --- 0 ?c --- frame1 "[nil]" Created frame: frame1 => T T algy> vf frame1 Frame1: Isa: things counters Current-count: 2