Sunteți pe pagina 1din 8

Name: Entry: 1

CSL 765: Introduction to (Logic and Functional) Programming


II semester 2014-15
Thu 30 Apr 2015 10:30-12:30 Major V-409A Max Marks 60

1. Please answer in the space provided on the question paper. The other sheets are only for rough work and
will not be collected.
2. You are not allowed to bring into the exam hall any paper nor any electronic gadgets such as computers,
mobile phones, iPads, Kindles etc.
3. Please keep your identity card with you. You may be asked for it at any time for verification.

1. (4+4 = 8 marks) Let p(x) = an xn + an1 xn1 + + a1 x + a0 with an 6= 0.0 and n 0 be a polynomial
with real coefficients. Design algorithms (written in functional pseudocode) which evaluate p(x) at any
given point x using only O(n) multiplications.
(a) p(x) is represented by a list [an , an1 , . . . , a0 ].
(b) p(x) is represented by a list [a0 , a1 , . . . , an ].
You are not allowed to obtain a solution to any part by reversing the list of coefficients.
Solution.
(a) Factor out the multiplications to get

p(x) = (...((an x + an1 )x + an2 )x + ...)x + a0

and define a tail-recursive function which requires only O(n) multiplications.

poly1(L, x) = poly tr(0, L, x) (1)


where 
p if L = nil
poly tr(p, L, x) = (2)
poly tr(px + h, T, x) if L = h :: T

(b) We begin with a trivial solution and then transform it.


0 if L = nil
revpoly0(L, x) =
h + x revpoly0(T, x) if L = h :: T

We transform revpoly0 in a completely standard fashion to obtain a tail-recursive version.

revpoly1(L, x) = revpoly1 tr(L, x, 1, 0) (3)


where 
s if L = nil
revpoly1 tr(L, x, p, s) = (4)
revpoly1(T, x, px, s + ph) if L = h :: T

Here p represents the value of xi for each i, 0 i n.


Name: Entry: 2

2. (2+2+2+2 = 8 marks) Consider the following Prolog program:

human(neha).
human(pooja).
human(ankit).
human(ankur).
human(ramesh).
dog(tommy).
dog(jimmy).
man(ankit).
man(ankur).
woman(X) :- human(X), not man(X).

What responses will the Prolog system yield for each of the following queries. Justify your answer in each
case.
(a) ?- woman(pooja).
(b) ?- woman(jimmy).
(c) ?- woman(ramesh).
(d) ?- woman(ankit).
Solution.

(a) ?- woman(pooja).
yes. Since human(pooja) is a fact and man(pooja) is not provable.
(b) ?- woman(jimmy).
no. Since human(jimmy) is neither a fact nor provable.
(c) ?- woman(ramesh).
yes. Since human(ramesh) is a fact and man(ramesh) is not provable.
(d) ?- woman(ankit).
no. Since human(ankit) is a fact and man(ankit) is also a fact and hence provable.
Name: Entry: 3

3. (4+(2+2+2)=10 marks) Let the type of binary trees (labelled by integers) in Prolog be defined by the
following clauses.

bintree(empty).
bintree(node(N, L, R)):- bintree(L),bintree(R).

1
_______/ \_______
| |
2 3
__/ \__ ___/ \___
| | | |
4 5 6 7
\_ __/ \__
| | |
8 9 10

The Euler Tour around a binary tree goes as follows. Starting from the root make a tour around the
whole tree in a counter-clockwise fashion so that the nodes being visited are always on the left as
you walk around the tree visiting each node. The euler tour of the above example produces the list
[1,2,4,8,4,2,5,2,1,3,6,9,6,10,6,3,7,3,1].
In this tour every non-leaf node is sighted 3 times the first time before the tour of the left subtree is
begun, the second time after the entire left-subtree has been toured before the tour of the right-subtree
begins and the third time after the right subtree has been toured. The three traversals may be filtered to
yield respectively the preorder, inorder and postorder traversals of the binary tree.

(a) Write Prolog program clauses to compute the Euler tour of a binary tree.
(b) Write clauses which yield preorder, inorder and postorder traversals of the binary tree by filtering
the Euler tour.

Solution. For an explanation of the logic behind this code refer to the explanations given in the ML version
(4).
We use the constructor p for pairs. The following is only a logic program and not Prolog code. So this
piece of code may not be directly executable.
(a) et(empty, []).
et(node(X, Left, Right), N) :- et(Left, L),
et(Right, R),
append([p(1,X)|L],[p(2,X)|R], LR),
append(LR, [p(3,X)], N).

append([],M,M).
append([H|T],M,[H|N]) :- append(T,M,N).

euler(empty, []).
euler(node(N, Left, Right), E) :-
bintree(node(N, Left, Right)),
et(node(N, Left, Right), T),
useful(T,ET),
seconds(ET,E).
useful([],[]).
useful([p(V,N)|T], [p(V,N)|S]) :- notsuccessive(T,p(V,N),S).
notsuccessive([],_,[]).
notsuccessive([p(V,N)|T],p(U,P), S) :-
V =:= U+1, notsuccessive(T,p(V,N), S).
notsuccessive([p(V,N)|T],p(U,P), [p(V,N)|S]) :-
Name: Entry: 4

V =\= U+1, notsuccessive(T,p(V,N), S).

seconds([], []).
seconds([p(X,Y)|T], [Y|U]) :- seconds(T, U).
(b) preorder(empty, []).
preorder(node(N, Left, Right), E) :-
bintree(node(N, Left, Right)),
et(node(N, Left, Right), ET),
filter1(ET, T1),
seconds(T1, E).

filter1([],[]).
filter1([p(V,N)|T], [p(V,N)|S]) :- V=:=1, filter1(T,S).
filter1([p(V,N)|T], S) :- V=\=1, filter1(T,S).
inorder(empty, []).
inorder(node(N, Left, Right), E) :-
bintree(node(N, Left, Right)),
et(node(N, Left, Right), ET),
filter2(ET, T1),
seconds(T1, E).

filter2([],[]).
filter2([p(V,N)|T], [p(V,N)|S]) :- V=:=2, filter2(T,S).
filter2([p(V,N)|T], S) :- V=\=2, filter2(T,S).
postorder(empty, []).
postorder(node(N, Left, Right), E) :-
bintree(node(N, Left, Right)),
et(node(N, Left, Right), ET),
filter3(ET, T1),
seconds(T1, E).

filter3([],[]).
filter3([p(V,N)|T], [p(V,N)|S]) :- V=:=3, filter3(T,S).
filter3([p(V,N)|T], S) :- V=\=3, filter3(T,S).
Name: Entry: 5

4. (4+(2+2+2)=10 marks) Solve problem 3 in a functional programming language (such as ML), where
binary trees are defined as follows.

datatype a bintree = empty | node of a * a bintree * a bintree

Solution. The following binary tree has


(a) multiple nodes with identical labels (e.g. 3 and 4),
(b) node 4 in the left subtree has an empty left subtree and
(c) node 5 and node 7 each have empty right subtrees.
Therefore it is necessary when defining an euler tour, to be able to distinguish between the three visits to
the same node and visits to different nodes with the same label.

1
_______/ \_______
| |
2 3
__/ \__ ___/ \___
| | | |
4 5 6 7
\_ _/ __/ \__ __/
| | | | |
8 4 9 10 3

The following function et is the Euler Tour. However, in order to facilitate an easy separation of the
various traversals required, it defines the tour as a list of pairs (visitnumber, nodelabel). Every node
n with node label nl would have three entries (1, nl), (2,nl) and (3, nl) in the list produced by et.
Each visit number encodes the position of the node in the required traversal.
(a) preorder
(b) inorder
(c) postorder

fun et (empty) = nil


| et (node (x, left, right)) = (1,x)::((et left)@
(2,x)::((et right)@[(3,x)]))

We then have
Preorder traversal : the sub-list of all first visits,
Inorder traversal : the sub-list of all second visits and
Postorder traversal : the sub-list of all third visits
Consider the list returned by the function et.
Notice that regardless of the out-degree of a node each node n has 3 entries (1,nl), (2,nl) and (3,
nl). In the case of a
leaf node : the three entries occur consecutively,
non-leaf node with empty left subtree : entries (1,nl) and (2, nl) occur consecutively but
(3, nl) occurs after the visits of the right sub-tree of the node,
non-leaf node with empty right subtree : entries (2,nl) and (3, nl) occur consecutively but
(1, nl) occurs before the visits of the left sub-tree of the node.
All nodes of out-degree 2 have no consecutive occurrences of visits.
Name: Entry: 6

If a parent and child have the same node label as in the following tree
a
_/ \_
| |
a a
even then the different visits to the different nodes with the the same label are distinguishable as
they would follow a balanced parentheses discipline. For this tree the list returned by et would be
[(1,a),(1,a),(2,a),(3,a), (2,a), (1,a),(2,a),(3,a), (3,a)]
The Euler tour of the above tree is [a,a,a,a,a] which are extracted from the ordered pairs [(1,a),
(1,a), (2,a), (1,a), (3,a)] where the first, middle and the last occurrence of a refer to the
visits to the root node and the other two are respectively those of the two leaf nodes.
Similarly the following trees have the same euler tour [a,b,a] but different lists of ordered pairs
returned by function et.

a a
_/ \_
| |
b b

[(1,a),(1,b),(2,b),(3,b),(2,a),(3,a)] [(1,a),(2,a),(1,b),(2,b),(3,b),(3,a)]
In these cases the euler tour is extracted from the pairs [(1,a),(1,b),(2,a)] and [(1,a),(1,b),(3,a)].
These ordered pairs are called useful; the other ordered pairs are called useless.
We filter out useless elements from the list returned by function et, by checking whether there are
consecutive occurrences of a node label whose visit numbers are increasing. This requires remembering
the last element that was (either useful or useless) processed.

fun et (empty) = nil


| et (node (x, left, right)) = (1,x)::((et left)@
(2,x)::((et right)@[(3,x)]))
local
fun useful ([], _, L) = rev (L: (int * int) list)
| useful (((h as (vn:int,nl:int))::t), p as (pvn, pnl), L) =
if (nl = pnl) (* the node labels are the same *)
andalso (vn = pvn+1) (* visit number increasing *)
then (* h is useless *) useful (t, h, L)
(* h is now the last element to be remembered *)
else (* h is useful *) useful (t, h, (h::L))
in fun euler empty = []
| euler T = (* T is a nonempty tree *)
let val etT = et T
in map #2 (useful (tl(etT), hd(etT), [hd(etT)]))
end
end

fun eqc x y = (x=y) (* Curried equality *)


fun preorder T = map #2 (List.filter ((eqc 1) o #1) (et T))
fun inorder T = map #2 (List.filter ((eqc 2) o #1) (et T))
fun postorder T = map #2 (List.filter ((eqc 3) o #1) (et T))
Name: Entry: 7

5. (8 marks) Let a be a constant symbol and f a unary function symbol. Use first-order resolution to
prove that = {p(a), p(f (f (a))), x[p(x) p(f (x))]} is unsatisfiable. Clearly state the most general
substitutions that need to be made at each step.
Solution. Assuming there exists at least the constant symbol a and there is a unary function symbol f , the
Herbrand universe consists of at least the following terms {a, f (a), f (f (a)), . . . , f n (a), . . .} where f ( a) refers
to the n-fold application of f to a. In other words f 0 (a) = a and f i+1 (a) = f (f i (a)) for each i 0. The
set of ground clauses of therefore is a some superset of {{p(a)}, {p(f 2 (a))}} {{p(f i (a)), p(f i+1 (a))} |
i 0}. We could take the clauses in the given order as follows:

{p(a)}, {p(f 2 (a))}, {p(a), p(f (a))}, {p(f (a)), p(f 2 (a))},
{p(f 2 (a)), p(f 3 (a))}, . . .
{Resolving on p(a)} {p(f 2 (a))}, {p(f (a))}, {p(f (a)), p(f 2 (a))}, . . .
{Resolving on p(f (a))} {p(f 2 (a))}, {p(f 2 (a))}, {p(f 2 (a)), p(f 3 (a))}, . . .
{Resolving on p(f 2 (a))} {}, {p(f 2 (a)), p(f 3 (a))}, . . .

by which the empty clause has been derived through propositional resolution.
To use first-order resolution one would proceed as follows

{p(a)}, {p(f 2 (a))}, {p(x), p(f (x))}


{substitution{a/x}} {p(a)}, {p(f 2 (a))}, {p(a), p(f (a))}, {p(x1 ), p(f (x1 ))}
{Resolving on p(a)} {p(f 2 (a))}, {p(f (a))}, {p(x1 ), p(f (x1 ))}, . . .
{substitution{f (a)/x1 }} {p(f 2 (a))}, {p(f (a))}, {p(f 2 (a))}, {p(x2 )), p(x2 )}, . . .
{}, {p(x2 )), p(x2 )}
Name: Entry: 8

6. (4+4+8=16 marks) For each of the ML functions defined below, find the most general polymorphic
type assignment.(where val id = fn x => x and o denotes function composition).
(a) val twice = fn x => fn y => (x (x y))
(b) val thrice = fn x => fn y => (x (x (x y)))
(c) val times = fn x => fn y => if y = 0 then id else x o (times x (y-1))

Solution.
(a,b) Since parts (a) and (b) have similar reasoning we give a common solution using lower-case greek
letters todenote type variables. Assuming x : , y : , (x (x (x y))) : , (x (x y)) : and (x y) : , we
get the following identities.
= 
= 
=
which by unification yields = = =  and hence we get

=
(x y) :
(x (x y)) :
fn y => (x (x y)) :
fn x => fn y => (x (x y)) : ( ) ( )
(x (x (x y))) :
fn y => (x (x (x y)) :
fn x => fn y => (x (x (x y)) : ( ) ( )

is completely arbitrary. Hence the most general polymorphic assignment for twice and thrice is
[( ) ( )].
(c) We use lower-case greek letters for type variables.
It is clear that id : [ ] which on instantiation yields . Further it is given that o :
[( ) ( ) ], which by instantiation yields ( ) ( ) .
It is clear from the definition of times that id, (times x y) and x o (times x y-1) should all
have the same type. From the type of o it follows that

x :
(times x y) :
x o (times x y 1) :

Unifying with , yields the substitution {/, /}. Applying this substitution we obtain

id :
x o (times x y 1) :
(times x y) :

Unifying with yields {/}. Composing all the substitutions so far yields {/, /, /}.
Hence
(times x y) :
x :
From the condition y > 0 we get y : int and hence times : ( ) int which on
generalization yields
times : [( ) int ( )]
as the most general polymorphic type of times.

S-ar putea să vă placă și