In the last post I mentioned a few of the design choices I've made for the Rete Network implementation. The rest of the engine is quite straight forward and simple. There's really not much to talk about but I'll show some code anyway ;-).
Another thing though, and a bit more interesting really, is that I have been thinking about whether or not I should try to stay true to CLIPS' behaviour and functionality in the misc engine functions as well.
For example, the agenda function currently returns a list of all activations on the agenda and CLIPS prints them but returns no value. Similarly there's facts which behaves like get-fact-list instead. Used at the REPL there's little difference in what the user sees but the reason I've made them different is because then I can write other functions on top of them.
Ok. As promised, some code. Here is the run function (which uses agenda):
|(defun run (&optional (limit -1))and here is the assert function (I'm shadowing the built-in assert function, still accessible as cl:assert though):
| (do* ((curr-agenda (agenda) (agenda))
| (execution-count 0 (+ execution-count 1))
| (limit limit (- limit 1)))
| ((or (eq limit 0)
| (= (length curr-agenda) 0)) execution-count)
| (let* ((activation (first curr-agenda))
| (rhs-func (make-sym "RHS-" (string (activation-rule activation))))
| (prod-mem (make-sym "PRODUCTION-" (string (activation-rule activation)) "-MEMORY")))
| (funcall rhs-func activation)
| (store '- activation prod-mem))))
|(defun assert (&rest facts)I hope that they are at least somewhat readable to others.
| (incf timestamp)
| (dolist (fact facts)
| (store '+ fact 'working-memory)
| (mapcar #'(lambda (node) (funcall node '+ fact timestamp))
| (gethash (type-of fact) (gethash 'root rete-network)))))
I've now finished the functions that make up the inference engine implementation. And it works... as long as you manually divide your program into alpha, beta and production nodes and connect them properly in the Rete Network ;-)