p(X,Y) :- r(Z), b(X).
p(X,Y) :- q(Y), b(X).
b(X) :- a(X).
results in (X = 1,Y = 2)
and (X = 0, Y = 2)
slide: DLP -- control (1)
When we pose our query, it is first
attempted to resolve the goal with the first
clause for p (which fails because cannot be resolved).
Then the second clause is tried, which leads to binding
Y to 2 (since is a fact), and gives us
two possible bindings for X,
due to the facts and .
(Variables are local to clauses and will be renamed
automatically to avoid clashes.)
Hence, the evaluation of the goal leads
to two consistent bindings, that may successively be obtained
As an example of somewhat more realistic clauses,
look at the list processing predicates member
and append in slide
List processing -- backtracking
member(X,[_|T]) :- member(X,T).
append([ H | T ],L,[ H | R ]):- append(T,L,R).
slide: DLP -- control (2)
Both predicates are specified in an inductive manner,
taking care of all possible cases.
For example, the first clause for member states
as a fact that an element is contained in a list
when it is the first element.
The second clause prescribes the recursive application
of member to the tail of the list if this is not the case.
Similarly, the clauses for append distiguish between
the case of appending a list to an empty list,
and the case of appending a list to a non-empty list.
This manner of specification closely adheres to
standard practice in mathematical logic
and has proven to be a powerful means to
develop knowledge-based systems
(such as expert systems) that rely on logical reasoning.