Reputation: 2505
If I have the following knowledge base, how do I add a cut to the parent_of term so that if an X is already determined to be a father, prolog won't attempt to "check" if X is also a mother?
father_of(max,john).
father_of(max,james).
father_of(max,gabe).
mother_of(june,john).
mother_of(june,james).
parent_of(X,Y) :- father_of(X,Y).
parent_of(X,Y) :- mother_of(X,Y).
For example, I want:
parent_of(max,Y) to be: Y=john, Y=james, Y=gabe
parent_of(june,Y) to be: Y=john, Y=james
For the first one, I don't want prolog to even attempt to check if max is a mother_of since it was already determined that he is a father_of.
I've already tried so many combinations including:
parent_of(X,Y) :- father_of(X,Y),!. <-- fixes an X and Y and thus will list only Y=john
parent_of(X,Y) :- !,father_of(X,Y). <-- works for parent_of(max,Y) but not parent_of(jane)
Is this even possible?
Upvotes: 3
Views: 317
Reputation: 10102
Doing such optimizations is extremely tricky. And Prolog is quite performing in such matters without any optimizations.
But as a general rule: You cannot simply set the cut into the program, you need to perform all kinds of tests in advance. I suspect that those tests are easily more expensive than the overhead you intend to remove. Here is an attempt. Honestly, I have not a very good feeling, because you should rather learn pure Prolog first.
So we assume that there is no solution to: ?- father_of(P,_), mother_of(P,_).
parent_of(F, C) :- ( atom(F), \+ \+ father_of(F, _) -> ! ; true ), father_of(F, C). parent_of(M, C) :- mother_of(M, C).
Is this really better? Maybe, and maybe not.
But in any case, you have already understood one important point: clever optimizations require extensive testing. I believe above is correct, but I am not even sure that this will be faster. After all, there are now two lookups for father_of/2
in place of one for father_of/2
and one for mother_of/2
. The naive version might be readily faster...
Upvotes: 1
Reputation: 2436
For this purpose, some Prolog systems offer a "soft cut" *->/2
:
parent_of(X,Y) :- father_of(X,Y) *-> true ; mother_of(X,Y).
In general for If *-> Then ; Else
, if the condition If
succeeds, Then
is executed, an on backtracking only alternative solutions for If
and Then
are returned. Only in the case that If
has no solutions at all, Else
is executed.
This requires you to know the mode, i.e., the calling pattern, of your predicate. In your case, if the mode of your predicate parent_of
is parent_of(++,?)
, then adding a soft cut is fine (assuming that names are unique or appear only for one gender). In the case of parent_of(++,++)
, even a regular cut would be fine, though unnecessary unless you have duplicate facts in your database (symmetry breaking is one use case for cuts). However, if you also have to cater for parent_of(-,?)
, then a cut really cuts away solutions.
In general: only use cuts when you are sure that the branch(es) of the search tree that you cut away contain no solutions, or only symmetric solutions that you are not interested in.
Upvotes: 0