Reputation: 73
If there is a rule that cannot be satisfied with all parameters, is there a standard way to get a partial match with just some of the parameters?
For example, the following query can not find a solution:
?- query(A, B, C).
false.
But a solution can be found if we don't try to unify on C:
?- query_best_effort(A, B, C).
A = alfa,
B = bravo ;
I have the following code that implements this functionality. But is there a more Prolog-way to do this?
fact1(alfa).
fact2(bravo).
fact3(charlie).
rule1(A) :-
fact1(A).
rule2(B) :-
fact2(B).
rule3(C) :-
fact3(C),
C \== charlie.
query(A, B, C) :-
rule1(A),
rule2(B),
rule3(C).
query_best_effort(A, B, C) :-
query_chain3(A, B, C);
query_chain2(A, B);
query_chain1(A).
query_chain3(A, B, C) :-
query(A, B, C).
query_chain2(A, B) :-
\+query_chain3(A, B, _),
rule1(A),
rule2(B).
query_chain1(A) :-
\+query_chain2(A, _),
rule1(A).
Upvotes: 3
Views: 242
Reputation: 18663
First, a comment on programming style. When using disjunctions (;/2
), always wrap them between parenthesis and avoid writing ;
at the end of a line. In your case:
query_best_effort(A, B, C) :-
( query_chain3(A, B, C)
; query_chain2(A, B)
; query_chain1(A)
).
This recommended style contributes to code readability.
You can also avoid using negation (\+/1
) by using instead the *->/2
soft-cut control construct implemented in several Prolog systems (a few systems, e.g. SICStus Prolog, implement it as an if/3
built-in predicate):
query_best_effort(A, B, C) :-
( query_chain3(A, B, C) *->
true
; query_chain2(A, B) *->
true
; query_chain1(A)
).
query_chain3(A, B, C) :-
query(A, B, C).
query_chain2(A, B) :-
rule1(A),
rule2(B).
query_chain1(A) :-
rule1(A).
The *->/2
control construct, unlike the standard ->/2
control construct, allows backtracking into the condition. When calling the query_best_effort/3
predicate, the query_chain2/2
predicate will only be called if there are no solutions for the query_chain3/3
goal and the query_chain1/1
predicate will only be called if there are no solutions for the query_chain3/3
and query_chain2/2
goals, which I assume was your intention with the use of disjunction and negation?
Sample call:
| ?- query_best_effort(A, B, C).
A = alfa
B = bravo
yes
Note, however, that *->/2
is, like negation, a non-logical control construct.
Upvotes: 4