Lecture 13
Today:
Quiz.
Reading questions.
Homework 7.
For next time:
Finish Homework 7.
Prolog exercises below.
More Prolog
declarative reading vs. operational reading
Declarative means that you read what the rules mean; operational means you think about how Prolog actually executes.
Here's a (modified) example from Programming in Prolog, by Clocksin and Mellish. (With apologies for the heterosexism of this example.)
boy(harry).
boy(ron).
boy(neville).
boy(draco).
girl(hermione).
girl(ginny).
girl(fleur).
pair(X,Y) :- boy(X), girl(Y).
What's the declarative reading of pair?
"X,Y is a pair if X is a boy and Y is a girl."
What's the operational reading; that is, what's going to happen when we ask pair(X,Y)?
Search for a X that makes boy(X) true.
Search for a Y that makes girl(Y) true.
Report a pair.
Backtrack and see if there's another Y that makes girl(Y) true.
When you run out of Ys, backtrack and start enumerating Xs.
When you run out of Xs, stop.
How to run Prolog in your head:
1) For a given predicate, start with the first rule/fact and work your way down.
2) Try to satisfy the first clause.
a) If you find a match, instantiate one or more variables, and move on to the next clause.
b) If the clause fails (doesn't find a match), backtrack to the previous clause and try to re-satisfy the goal.
The cut
The cut is a special predicate, spelled !, that
1) succeeds immediately and moves on to the next clause, but
2) if you backtrack and hit a cut, it fails immediately and
does _not_ try to resatisfy the previous goal.
What effect does this have on the previous example:
pair(X,Y) :- boy(X), !, girl(Y).
What about
pair(X,Y) :- !, boy(X), girl(Y).
What about
pair(X,Y) :- boy(X), girl(Y), !.
A common use of cut is the cut-fail combination, which means "if you get this far, you lose".
pair(X,Y) :- boy(X), girl(Y), !, fail.
not
Which suggests an implementation of one version of not:
not(P) :- call(P), !, fail.
not(P).
call(P) means "try to satisfy P"
What happens if it succeeds? What happens if it fails?
Notice that not(girl(X)) does not enumerate all instantiations of X for which girl(X) is false, which is what you might have meant/wanted.
How would you do that?
Lists
Prolog provides lists as a basic type, with the usual syntax:
[1, 2, 3] is a list of three elements.
In a predicate, you can match the first element and the rest:
car([X|Y], X).
This means that the predicate car is true if the first argument is a list with X as the first element and Y as the rest, and if the second argument matches X.
Likewise with cdr and cons.
cdr([X|Y], Y).
cons(X, R, [X|R]).
member
You seldom use car and cdr explicitly. For example, here is an implementation of the built-in predicate member:
member(X,[X|R]).
member(X,[Y|R]) :- member(X,R).
This checks membership (obviously):
| ?- member(3, [1,2,3]).
true
But it also enumerates the members:
yes
| ?- member(X, [1,2,3]).
X = 1
X = 2
X = 3
And even generates all the lists that contain a given element!
| ?- member(1, X).
X = [1|_] ? ;
X = [_,1|_] ? ;
X = [_,_,1|_] ? ;
X = [_,_,_,1|_] ?
and so on...
Write a set of rules that define a predicate cross/3 that takes list A and B, and a pair P, and unifies if P is a member of the Cartesian product of A and B.
Takeout
Type in these rules:
takeout(X,[X|R],R).
takeout(X,[F|R],[F|S]) :- takeout(X,R,S).
And try these examples:
1) takeout(2,[1,2,3],L).
2) takeout(X,[1,2,3],L).
3) takeout(3, W, [a,b,c]).
What's a better name for "takeout" in example 3?
Append
Here's a definition of myappend (append is a built-in predicate):
myappend([],X,X).
myappend([X|R],Y,[X|S]) :- myappend(R,Y,S).
Try these examples:
1) myappend([1,2,3],[4,5],A).
2) myappend([1,2,3],W,[1,2,3,4,5]).
3) myappend(A,[5],[1,2,3,4,5]).
4) myappend(X, Y, [1,2,3,4,5]).
Can you write a definition for myreverse? Hint: think about the tail-recursive version of reverse we did in Racket, then write a 3-argument version of myreverse so that
myreverse([1,2,3], [], [3,2,1])
and
myreverse([2,3], [1], [3,2,1])
are true.