Prolog - part 2

Backward chaining & resolution principle; equality and arithmetic in Prolog; subgoal ordering; and functions vs. predicates Material adapted from Mark Hills’ Organization of Programming Languages

Lesson plan

Prolog

Prolog interpreter

Prolog interpreter

Prolog interpreter

Prolog interpreter

Prolog interpreter

Prolog interpreter

<a name='figure12_2'>Figure 12.2</a>. Prolog uses backtracking search as a tree, with the top-level goal as the root and the leaves as the facts. In this example, the query ?- path(a, a) will never terminate (the interpreter will never find the trivial branch).
Figure 12.2. Prolog uses backtracking search as a tree, with the top-level goal as the root and the leaves as the facts. In this example, the query ?- path(a, a) will never terminate (the interpreter will never find the trivial branch).

Prolog interpreter

Prolog interpreter

parent(a,b). % a is the parent of b

parent(a,d).

parent(a,k).

parent(k,l).

parent(k,m).

parent(b,e).

parent(b,f).

parent(f,g).

parent(f,h).

parent(f,i).

ancestor(X,Y) :- parent(X,Y).

ancestor(X,Y) :- parent(Z,Y), ancestor(X,Z).

<<trace>>

Prolog interpreter

parent(a,b). % a is the parent of b

parent(a,d).

parent(a,k).

parent(k,l).

parent(k,m).

parent(b,e).

parent(b,f).

parent(f,g).

parent(f,h).

parent(f,i).

ancestor(X,Y) :- parent(Z,Y), ancestor(X,Z).

ancestor(X,Y) :- parent(X,Y).

Prolog interpreter

parent(a,b). % a is the parent of b

parent(a,d).

parent(a,k).

parent(k,l).

parent(k,m).

parent(b,e).

parent(b,f).

parent(f,g).

parent(f,h).

parent(f,i).

ancestor(X,Y) :- ancestor(X,Z), parent(Z,Y).

ancestor(X,Y) :- parent(X,Y).

ICE

How is this resolved?

?- mortal(jane). /* not in database */

Equality in Prolog is defined in terms of “unifiability”

For the sake of convenience, the goal may be written as A = B

?- a = a.

true. % constant unifies with itself

?- a = b.

false. % but not with another constant

?- foo(a, b) = foo(a, b).

true. % structures are recursively identical

?- X = a.

X = a. % variable unifies with constant

?- foo(a, b) = foo(X, b).

X = a. % arguments must unify

Equality in Prolog is defined in terms of “unifiability”

?- A = B.

A = B. % response from interpreter

?- A = B, A = a, B = Y.

% unifying A and B before binding a to A

% the interpreter will linearize the string of unifications and make it clear that all three variables are equal to a:

A = B, B = Y, Y = a.

Arithmetic via the is keyword

fact(0,1).

fact(N,X) :- M is N-1, fact(M,Y), X is Y * N.

?- fact(5,X).

fact(5,X) :- M is 5-1, fact(M,Y), X is Y * 5.

fact(5,X) :- 4 is 5-1, fact(4,Y), X is Y * 5.

fact(5,X) :- 4 is 5-1, fact(4,24), X is 24 * 5.

fact(5,120) :- 4 is 5-1, fact(4,24), 120 is 24 * 5.

Subgoal ordering

Order of sub-goals is important! Why does this happen?

badfact(0,1).

badfact(N,X) :- badfact(M,Y), M is N-1, X is Y * N.

?- badfact(5,X).

ERROR: Arguments are not sufficiently instantiated

^ Exception: (8) 0 is _G278-1 ?

badfact(5,X) :- badfact(M,Y), M is 5-1, X is Y * 5.

badfact(5,X) :- badfact(0,1), 0 is 5-1, X is 1 * 5.

%oops

Subgoal ordering

Order of sub-goals is important, as shown in Figure 12.1 from Programming Language Pragmatics, by Michael L. Scott. Why does this happen?

<a name='figure12_1'>Figure 12.1</a>. Backtracking search in Prolog. The tree of potential resolutions consists of alternating AND and OR levels. An AND level consists of subgoals from the right-hand side of a rule, all of which must be satisfied. An OR level consists of alternative database clauses whose head will unify with the subgoal above; one of these must be satisfied. The notation _C = _X is meant to indicate that while both C and X are uninstantiated, they have been associated with one another in such a way that if either receives a value in the future it will be shared by both.
Figure 12.1. Backtracking search in Prolog. The tree of potential resolutions consists of alternating AND and OR levels. An AND level consists of subgoals from the right-hand side of a rule, all of which must be satisfied. An OR level consists of alternative database clauses whose head will unify with the subgoal above; one of these must be satisfied. The notation _C = _X is meant to indicate that while both C and X are uninstantiated, they have been associated with one another in such a way that if either receives a value in the future it will be shared by both.

badfact(0,1).

badfact(N,X) :- badfact(M,Y), M is N-1, X is Y * N.

?- badfact(5,X).

ERROR: Arguments are not sufficiently instantiated

Exception: (8) 0 is \_G278-1 ?

badfact(5,X) :- badfact(M,Y), M is 5-1, X is Y * 5.

badfact(5,X) :- badfact(0,1), 0 is 5-1, X is 1 * 5.

%oops

Structures and patterns

Lists

Prolog lists are very similar to Python lists

Lists

The vertical-bar notation is particularly handy when the tail of the list is a variable:

member(X, [X | _]). % _ is a placeholder for a variable that is not needed anywhere in the clause

member(X, [_ | T]) :- member(X, T).

sorted([]). % empty list is sorted

sorted([_]). % singleton is sorted

sorted([A, B | T]) :- A =< B, sorted([B | T]). % built-in =< predicate operates on numbers

Functions vs. predicates

Prolog resolution does not distinguish between “input” and “output” arguments

append([], A, A).

append([H | T], A, [H | L]) :- append(T, A, L).

?- append([a, b, c], [d, e], L).

L = [a, b, c, d, e].

?- append(X, [d, e], [a, b, c, d, e]).

X = [a, b, c] ;

false.

?- append([a, b, c], Y, [a, b, c, d, e]).

Y = [d, e].

Functions vs. predicates

This example highlights the difference between functions and Prolog predicates:

List example: mylength

The length predicate is built in.

mylength([],0).

mylength([H|T],X) :- mylength(T,Y), X is Y + 1.

?- mylength([2,3,4,5],X).

X = 4 ;

No

This example looks like badfact, in that the is clause happens after the recursion. Why is this safe?

List example: sum list

sumlist([],0).`

sumlist([H|T],X) :- sumlist(T,Y), X is Y + H.

?- sumlist([2,3,4,5],X).

X = 14

List example: append

myappend([],X,X).

myappend([H|T],X,[H|Z]) :- myappend(T,X,Z).

?- myappend([2,3,4],[5,6,7],X).

X = [2, 3, 4, 5, 6, 7] ;

No

26 ?- myappend(X,[2,3],[1,2,3,4]).

No

27 ?- myappend(X,[2,3],[1,2,3]).

X = [1] ;

No

List example: reverse

Accumulator recursion works in Prolog, too!

myreverse(X,Y) :- myrevaux(X,Y,[]).

myrevaux([],Y,Y).

myrevaux([HX|TX],Y,Z) :- myrevaux(TX,Y,[HX|Z]).

?- myreverse([2,3,4],Y).

Y = [4, 3, 2]

myreverse([2,3,4],Y) \(\rightarrow\) myrevaux([2,3,4],Y,[])

\(\rightarrow\) myrevaux([3,4],Y,[2])

\(\rightarrow\) myrevaux([4],Y,[3,2])

\(\rightarrow\) myrevaux([],Y,[4,3,2])

\(\rightarrow\) myrevaux([],[4,3,2],[4,3,2])

\(\rightarrow\) myreverse([2,3,4],[4,3,2])

Pairs

pair((X,Y)).

key((X,Y),X).

value((X,Y),Y).

assoc(X,Y,[H|T]) :- key(H,X), value(H,Y).

assoc(X,Y,[H|T]) :- assoc(X,Y,T).

?- assoc(2,X,[(3,hi),(4,there),(2,guys)]).

X = guys

?- assoc(X,there,[(3,hi),(4,there),(2,guys)]).

X = 4

Activity!

ICE

Logic programming in perspective

Summary

Models of computing

Reminders: