Re: Asserting a fact when triggering a rule
"Richard A. O'Keefe" <[email protected]>
| Newsgroups | gmane.comp.ai.prolog.swi |
|---|---|
| Message-ID | <[email protected]> |
It sounds as though you are trying to do forward chaining.
Some Prolog textbooks talk about that.
In ordinary Prolog, there is no such thing as "a rule being
triggered". Predicates are *called* or they don't happen.
The obvious question is
WHAT IS THE REAL PROBLEM THAT YOU ARE TRYING TO SOLVE?
Forward chaining is *one* way to solve some problems,
but it isn't the only way, and for your application, it
might be very far from the best way. Forward chaining
has a nasty habit of cluttering up memories with facts that
are true but irrelevant.
Any time you want to do anything tricky with the database,
the answer is to NOT make assertions directly.
Instead of :- assert(person(fred)).
you do
:- assert_person(fred).
So for example you might have
:- dynamic vertebrate/1.
:- dynamic mammal/1.
:- dynamic person/1.
assert_vertebrate(Thing) :-
/* validation code goes here */
assert(vertebrate(Thing)).
assert_mammal(Thing) :-
/* validation code goes here */
assert_vertebrate(Thing),
assert(mammal(Thing)).
assert_person(Thing) :-
/* validation code goes here */
assert_mammal(Thing),
assert(person(Thing)).
In this case, there's a much better scheme, which was
folklore, but Chris Mellish formalised it. I shan't
present the full version here.
:- dynamic thing_type/2.
assert_type(Thing, Type1) :-
( type(Thing, Type2)
-> ( % If Type2 is already a special case
% of Type1, we have nothing to do.
numbervars(Type2, 0, _),
Type1 = Type2
-> true
; % Combine Type1 and Type2
retractall(type(Thing, _)),
Type1 = Type2,
assert(type(Thing, Type1))
)
; assert(type(Thing, Type1))
).
assert_vertebrate(Thing) :-
assert_type(Thing, vertebrate(_)).
assert_mammal(Thing) :-
assert_type(Thing, vertebrate(mammal(_))).
assert_person(Thing) :-
assert_type(Thing, vertebrate(mammal(person(_)))).
vertebrate(Thing) :-
type(Thing, vertebrate(_)).
mammal(Thing) :-
type(Thing, vertebrate(mammal(_))).
person(Thing) :-
type(Thing, vertebrate(mammal(person(_)))).
It's pretty obvious how this handles a single-inheritance
type hierarchy; the full scheme is rather richer.
In general, suppose you have a set of forward chaining rules
like
Head <- G1, {F1}, ..., Gn, {Fn}.
where the Gi are calls to dynamic predicates and
the {Fi} are ordinary Prolog called for filtering.
Let's take an example like
happy(P) <-
married(P, S),
happy(S).
We can turn this into an assertion rule for each Gi.
assert_Gi :-
( \+ Gi
assert(Gi),
G1, F1, ..., <omit Gi>, Gn, Fn,
assert_Head,
fail
; true
).
In this case,
assert_married(P, S) :-
( \+ married(P, S),
assert(married(P, S)),
happy(S),
assert_happy(P),
fail
; true
).
assert_happy(S) :-
( \+ happy(S),
assert(happy(S)),
married(P, S),
assert_happy(P),
fail
; true
).
What we actually have to do is to coalesce these; it's not
quite as simple as I've made it seem.
You'll note that my examples don't include equivalents
of retract/1. That takes us into Truth Maintenance System
territory, and if you _really_ want things triggered when
facts are added and removed, you may be after something
more like Drools, which I gather is (partly) what Pyke is.
Another approach is to rethink your approach to your problem.
Consider the logical formula
positive(M) & succ(M, N) -> positive(N).
and think of it as a forward chaining rule.
Assert positive(1).
Watch your program race away, dutifully recording
positive(2), positive(3), positive(4), ....
But the same rule works perfectly well as a backward chaining rule.