Prolog Supervision Work
Table of Contents
Please send in work by e-mail 48 hours before the supervision. Scanned copies are okay, you don't have to typeset the work, as long as it is legible. Please convert any Microsoft Word documents to PDF before sending.
Have a look at Notation of Predicate Descriptions to see
what all the +
, -
and ?
means in
e.g. member(?Elem, ?List)
.
Supervision 1
Question 1.1 (2R.1 and 2R.2)
Specify the rules Prolog uses for unification.
Does the following unify, and what substitutions does it
have? What is the fully unified term?
tree(C,tree(Z,C))
and
tree(tree(tree(1,2),A,B),tree(C,tree(E,F,G)))
1.1.1 Optional
If you are keen, can you do this in Standard ML? The answer
is a function of type substitution -> term * term -> substitution option
. Recall that an option
is the
built-in type that is either NONE
or SOME a
. Substitutions are a mapping from variables to terms, and
to make your life easier, you might want to use an
association-list, i.e. a list of variable/term pairs.
datatype term = Atom of string | Var of variable | Comp of string * term list and variable = User of string | Internal of int (* Pretty-printing *) fun pp_term (Atom s) = s | pp_term (Var s) = pp_var s | pp_term (Comp (s, ts)) = s ^ "(" ^ (String.concatWith ", " (map pp_term ts)) ^ ")" and pp_var (User s) = s | pp_var (Internal i) = "%"^Int.toString i
Question 1.2 (2S.1 and 2S.2)
Describe the possibilities of unifying a(A)
with A
, and the
advantages of each. How does unification relate to type-inference as
found in Standard ML? What would be the term with a type that would
involve unifying a(A)
with A
?
Question 1.3
Can you implement making change, from your ML course?
?- change(20, [8, 5, 3], X). X = [8, 3, 3, 3, 3] ; X = [5, 5, 5, 5] ; X = [5, 3, 3, 3, 3, 3] ;
Question 1.4 (8S.1 and 8S.2)
Consider the implementation of append given below. Explain how it should be used and draw out a search tree for a representative example.
append([],A,A). append([H|T],A,[H|R]) :- append(T,A,R).
You are given two implementations of member
member(H,[H|_]). member(H,[_|T]) :- member(H,T). % or, alternatively member(X,Y) :- append(_,[X|_],Y).
- Discuss how the second implementation compares with the use of 'partial application' from functional programming, and how it differs.
- If the two implementations are equivalent, should a programmer simply inline all calls to member with a call to append?
- If the two implementations are equivalent, what advantages does one form of expression have over the other?
- Prove (informally) why the two are logically equivalent.
Question 1.5 (8S.3 and 8S.4)
What is the purpose of the following clauses:
a([]). a([H|T]) :- a(T,H). a([],_). a([H|T],Prev) :- H >= Prev, a(T,H).
What does the following do and how does it work?
b(X,X) :- a(X). b(X,Y) :- append(A,[H1,H2|B],X), H1 > H2, append(A,[H2,H1|B],X1), b(X1,Y).
Question 1.6
Given the following clauses,
a(4). a(x). b(3,x). b(1,7). c(A, B, C) :- a(A), b(B, _), !, a(D). c(A, _, B) :- b(A, B).
- List the solutions reported by Prolog to the query
c(P, Q, R)
, for each giving any binding of variables that occurs. [2 marks] - Explain whether the query
c(P, Q, R)
can bindP
tox
. [1 mark] - List the solutions to the query
c(1, 6, R)
, for each giving any binding of variables that occurs. [1 mark]
Question 1.7
We represent a binary tree using a term of the form [Left,NodeName,Right]
or null
. Here is an example perhaps recording a tree of excursions between
countries:
[[null,de,null],uk,[[null,ua,null],pt,null]]
Write a predicate p(+Tree, +NNames, -Ps)
where
Tree
is a tree as above, NNames
is a
list of node names, and Ps
is to be unified with
a list of elements of the form [N,P]
where
N
is in NNames
and P
is
N
’s parent in Tree
. [For example,
supposing T is bound to the above tree, the query
p(T, [ua, za], Ps)
binds Ps
to
[[ua, pt]]
.] [5 marks]
Supervision 2
Question 2.1
Recall question 7 from supervision 1. If you have access to my implementation of the answer, you might like to consider that code (it recurs first on the tree, then the list).
We represent a binary tree using a term of the form
[Left,NodeName,Right]
ornull
. Here is an example perhaps recording a tree of excursions between countries:[[null,de,null],uk,[[null,ua,null],pt,null]]Write a predicate
p(+Tree, +NNames, -Ps)
whereTree
is a tree as above,NNames
is a list of node names, andPs
is to be unified with a list of elements of the form[N,P]
whereN
is inNNames
andP
isN
’s parent inTree
. [For example, supposing T is bound to the above tree, the queryp(T, [ua, za], Ps)
bindsPs
to[[ua, pt]]
.] [5 marks]
- Difference lists are a powerful tool for increasing efficiency but they address a very specific problem. Does this issue arise in your implementation of p/3? In other words, can p/3 be made more efficient using difference lists and why (or why not)? You should also look at my answer to the previous question (if I have e-mailed my answers out to you.) [2 marks]
- Regardless of your answer above, write a predicate
pdl/3
that behaves just likep/3
but uses difference lists in its implementation. [4 marks]
Question 2.2
A binary tree has nodes whose values are non-empty lists. A tree node
is represented using a term that takes the form
Left-SomeList+Right
or nil
(note that
parentheses can be used to enforce precedence). For example, the
following term would be a valid tree:
nil-[root,1,2]+(nil-[child,4]+nil)
- Write a predicate
preorder(+Tree,-ValueList)
that unifiesValueList
with a list containing all of the tree nodes’ values from a pre-order tree walk (i.e. emit node value, then left subtree, then right subtree). The predicate should fail if any of the tree nodes’ values are not of the correct form. For the above example tree,preorder/2
would unifyValueList
with[[root,1,2],[child,4]]
. You may assume thatappend/3
has been defined already. [6 marks] - Now write a predicate
preorderdl/2
that behaves exactly like yourpreorder/2
predicate (i.e. returns a normal list), but in its implementation makes use of difference lists, instead of queryingappend/3
. [6 marks]
Question 2.3
Consider the predicate p/4 defined as:
p(_,[],[],[]). p(P,[X|Xs],[X|L1],L2) :- X < P, p(P,Xs,L1,L2). p(P,[X|Xs],L1,[X|L2]) :- X >= P, p(P,Xs,L1,L2).
- What does
p/4
do? Show an example query and response. [2 marks] - What types of terms cannot be used as the first
p/4
argument? [2 marks] - Describe where and why red and green cuts can be
usefully employed within the
p/4
predicate, modifying the predicate if necessary. [3 marks] The quicksort algorithm requires selection of a pivot value, forming two sublists, and then building the sorted list from the results of recursively quicksorting the sublists. The first sublist contains all elements that are less than the pivot value. The second contains the remaining elements.
Implement quicksort in a predicate
qs(+In,-Out)
that bindsOut
to a sorted version of the listIn
, and uses thep/4
predicate above. Assume thatIn
only contains numbers. Do not optimise your choice of pivot value: just use the head of the input list. [5 marks]
- What does
The predicate
flatten(+In,-Out)
is defined to expect thatIn
be unified with a list that may contain elements that are themselves lists (and likewise for those lists, up to any depth). In such cases,Out
is unified with a list that never has elements that are lists—all lists are expanded in place. For example,flatten([1,2,[3,4],[[5],6]],[1,2,3,4,5,6])
would be true.Implement the
flatten/2
predicate. When given a valid query, your solution should not provide any spurious results. [6 marks]
Question 2.4
In this question you should ensure that your predicates behave appropriately with backtracking and avoid over-use of cut. You should provide an implementation of any library predicates used. You may not make use of extra-logical built-in predicates such as findAll. Minor syntactic errors will not be penalised.
Rewrite choose without using cut. [2 marks]
choose(0,_,[]) :- !. choose(N,[H|T],[H|R]) :- M is N-1, choose(M,T,R). choose(N,[_|T],R) :- choose(N,T,R).
- Explain the operation of not (also written as \+) in a Prolog program. [1 mark]
Rewrite chooseAll without using not and cut (!). [10 marks]
chooseAll(N,L,Res) :- chooseAll(N,L,[],Res). chooseAll(N,L,Seen,Res) :- choose(N,L,R), not(member(R,Seen)), !, chooseAll(N,L,[R|Seen],Res). chooseAll(_,_,Res,Res).
- What is Last Call Optimisation and why is it beneficial? [3 marks]
Rewrite pos to enable Last Call Optimisation. [2 marks]
pos([],[]). pos([H|T],[H|R]) :- H >= 0, pos(T,R). pos([H|T],R) :- H < 0, pos(T,R).
Question 2.5
Solve the following constraint satisfaction problems using a constraint logic programming library. (You could solve it without the library, but why make your task more difficult).
Assign colours red, green, and blue to each of the nodes, so that no two connected nodes have the same colour.
graph G { /* Graphing settings */ layout=neato; splines=true; node [shape=circle]; /* The edges, so that you can easily encode them with search&replace */ 1 -- 2; 1 -- 3; 1 -- 6; 2 -- 6; 3 -- 6; 3 -- 8; 3 -- 9; 4 -- 5; 4 -- 6; 4 -- 9; 5 -- 8; 5 -- 9; 5 -- 10; 6 -- 7; 6 -- 10; 7 -- 8; /* The len=2 is just for the graphing software */ 8 -- 9 [len=2]; }
For each edge, how many solutions are there, if you remove that edge? Ignore permutations of existing solutions (e.g. ignore replacing all red with blue, blue with green, and green with red, and the like).
You might find
findall(+Template, :Goal, -Bag)
useful, e.g.?- member(X, [1,2,3,4,5]), 0 is X mod 2. % Find even numbers % X = 2 ; % X = 4 ; ?- findall(X, (member(X, [1,2,3,4,5]), 0 is X mod 2), Evens). % Evens = [2, 4].
It is an extra-logical predicate, and it might not always be allowed in the exam though.