veganjay
veganjay

Reputation: 73

Prolog Partial Matching

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

Answers (1)

Paulo Moura
Paulo Moura

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

Related Questions