Prolog Programming (Volume 5)

30
Prolog Programming (Volume 5) Dr W.F. Clocksin

description

Prolog Programming (Volume 5). Dr W.F. Clocksin. List cells + Unification = Great Idea. Normally we use ‘proper lists’, which are defined according to the theory of lists: A list of length 0 is [] A list of length N is [H|T] where T is a list of length N-1 - PowerPoint PPT Presentation

Transcript of Prolog Programming (Volume 5)

Page 1: Prolog Programming (Volume 5)

Prolog Programming(Volume 5)

Dr W.F. Clocksin

Page 2: Prolog Programming (Volume 5)

List cells + Unification = Great IdeaNormally we use ‘proper lists’, which are defined according to the theory of lists:A list of length 0 is []A list of length N is [H|T] where T is a list of length N-1

This is good when we only want to cons elements onto the from of the list. But, Prolog variables make it possible to do something else:

Page 3: Prolog Programming (Volume 5)

List cells + Unification = Great Idea

zap([a, b, c, Z], Z, R)

zap(X, d, X)

so, R = [a, b, c, d]More generally,

zap([a, b, c | Z], Z, R)

zap(X, [d, e, f], X)

Page 4: Prolog Programming (Volume 5)

Difference StructuresProbably one of the most ingenious programming techniques ever invented (by Sten-Åke Tärnlund, 1975) yet neglected by mainstream computer science.

A way of using variables as ‘holes’ in data structures that are filled in by unification as the computation proceeds.Most examples here will be with difference lists.

Will introduce by showing the usual (non difference list) algorithm for concatenating lists.

Page 5: Prolog Programming (Volume 5)

Concatenating two lists (WS 25)?- append([a, b, c], [d, e, f], X).X = [a, b, c, d, e, f]

append([], L, L).append([X|Y], T, [X|Z]) :- append(Y, T, Z).

Notice this is tail recursive. This definition is possible because of logical variables. Definitions in other languages cannot be tail recursive because the last call is cons, not append.

Page 6: Prolog Programming (Volume 5)

Concatenating two lists (WS 25)What do these goals do??- append([a, b, c], X, [a, b, c, d, e, f]).?- append([a, b, c], [d, e, f], X).?- append(X, Y, [a, b, c, d, e, f]).

X = [] Y = [a, b, c, d, e, f];X = [a] Y = [b, c, d, e, f];X = [a, b] Y = [c, d, e, f]; . . . .X = [a, b, c, d, e, f] Y = []

Page 7: Prolog Programming (Volume 5)

Linearising (flattening) a list (WS 25)Flattening a list is a way of listing the leaf nodes (finding the fringe) of tree-structured data. For example,[ a, b], [c, d, [e, f], g], h, [i, [k] ] ]

flattens to [a, b, c, d, e, f, g, h, i, k]

Here is the usual program:flatten([], []).flatten([H|T], L3) :-

flatten(H, L1), flatten(T, L2), append(L1, L2, L3).flatten(X, [X]).This is very inefficient: how many calls to append?

Page 8: Prolog Programming (Volume 5)

Instead, Difference ListsThe idea is to represent a list segment as a pair of terms, the front and the back. For example, the list segment L1-L2 has the elements a,b:

L1 refers to the front of the list, and L2 refers to the back, often a variable. This variable can be used as a ‘hole’ into which another term may be instantiated. If the other term has a hole too, this can be useful.

L1 L2

ab

L1 [a, b | L2]

Page 9: Prolog Programming (Volume 5)

Example: Append L1-L2 to L3-L4L1 L2

ab

L3 L4

de

c

Call the result X-Y. The following are true about X-Y:

X should co-refer with L1 (to be the front of the list)

Y should co-refer with L4 (to be the back of the list)

L2 should co-refer with L3 (to join the lists together)

Page 10: Prolog Programming (Volume 5)

Make it so:L1 L2

ab

L3 L4

de

c

X Y

In Prolog we can use this idea to implement constant-time appending of two lists. As suggested above, doing this is simply a matter of re-arranging variables.

Page 11: Prolog Programming (Volume 5)

Definition of appThe goal app(L1,L2,L3,L4,X,Y) succeeds when the difference lists made from L1 and L2 and concatenated with the difference lists made from L3 and L4 to give the resulting difference list made from X and Y.

app(L1, L2, L2, L4, L1, L4).Example execution:

?- app([a, b, c | Z1], Z1, [d, e, | Z2), Z2, X, Y).X = [a, b, c, d, e | Y].

A better way to see the arrangement of variables is like this:

app(A, B, B, C, A, C).

Page 12: Prolog Programming (Volume 5)

Usual notation of difference listsDenote difference list with front L1 and back L2 as L1-L2, where - is an infix binary operator. This represents difference lists as a single term, so cuts down on the arity of procedures. Programs are clearer, as in this definition of app:.

app(A-B, B-C, A-C).Example execution:

?- app([a, b, c | Z1]-Z1, [d, e, | Z2)-Z2, X-Y).X-Y = [a, b, c, d, e | Y]-Y.

The extra space taken by the ‘-’ functor is not a serious problem in practice. But then, why use app when you can do the rearrangement of variables in-place where it is needed in a program!

Page 13: Prolog Programming (Volume 5)

SummaryL-L is the null difference list

[a | Z]-Z is the difference list containing ‘a’. Similarly, [a, b, c | Z]-Z is the difference list containing a, b, c.

Unifying the difference list X with Y-[] will rectify the list, that is, turn it into a proper list. For example. unifying [a, b, c | Z]-Z with Y-[] instantiates Y to [a, b, c].

Page 14: Prolog Programming (Volume 5)

Linearising (flattening) a list (WS 28)

flatten(X, Y) :- flatpair(X, Y-[]).

flatpair([], L-L).

flatpair([H|T], L1-L3) :-

flatpair(H, L1-L2), flatpair(T, L2-L3).

flatpair(X, [X|Z]-Z).

Notice the pattern of ‘stitching’ elements together.

Page 15: Prolog Programming (Volume 5)

Linearising (flattening) Trees (WS 29)

First, how to represent a binary tree.Leaves are any terms.Nodes are constructed from the term n(t1, t2) where terms t1 and t2 are trees called the left branch and the right branch. For example, the term n(n(a,b),n(c,n(d,e))) names the tree:

n

n n

a b c n

d e

Page 16: Prolog Programming (Volume 5)

Linearising (flattening) Trees (WS 29)

Linearising a tree. This example will add interest by doing a partial map: only the integers will be linearised.The naive method (using append):

lintree(n(A,B), L1) :-lintree(A, LA),lintree(B, LB),append(LA, LB, L1).

lintree(I, [I]) :- integer(I).

lintree(X, []).

Page 17: Prolog Programming (Volume 5)

Linearising (flattening) Trees (WS 29)

The difference list method:

lintree(n(A,B), L1) :-lintree(A, LA),lintree(B, LB),append(LA, LB, L1).

lintree(I, [I]) :- integer(I).

lintree(X, []).

Page 18: Prolog Programming (Volume 5)

Worked exercise for WS 29/* picktree(tree, list of apples, list of pears) */

picktree(apple, [apple | L]-L, P-P).

picktree(pear, A-A, [pear | L]-L).

picktree(n(L,R), A1-A3, P1-P3) :-

picktree(L, A1-A2, P1-P2),

picktree(R, A2-A3, P2-P3).

picktree(Other, A-A, P-P).

Page 19: Prolog Programming (Volume 5)

Normalising Sum Expressions (WS 30)

The sums (a+b)+(c+d) and (a+(b+(c+d))) may be normalised into a standard form that associates on the left: a+b+c+d, or equivalently, ((a+b)+c)+d. This is only possible because they are sums.

+

+

+ab

c d

+

+ +

a b c d

+

+

+

a b

cd

Page 20: Prolog Programming (Volume 5)

Define a predicate normsumThe goal normsum(X,Y) succeeds when sum expression X normalises to Y. One method is to first flatten the sum, and then build up the normalised version in another pass. Here is flat(A,B,C), which flattens the sum into the difference list B-C:flat(X+Y), R1-R3) :-

!, flat(X, R1-R2), flat(Y, R2-R3).

flat(X, [X|Z] - Z).

The difference ‘thread’ is R1R2R3, and note the ‘!’ to commit to the first clause when a sum node is encountered.

Page 21: Prolog Programming (Volume 5)

Building up the normalised sumThis is not obvious. One way is to accumulate the ‘tree so far’, giving it a new parent node each time an element is found. This ‘stacks up’ nodes instead of inserting them. A ‘base case’ for nil is not needed because the flattened list will have at least two elements.

build([X], T, T+X) :- !.

build([H | L], T, Z) :- build(L, T+H, Z).

Page 22: Prolog Programming (Volume 5)

Putting them togetherNow normalise by flattening then building. The ‘tree so far’ in the second argument of build needs to be initialised with the first element of the flattened list:

normalise(A, C) :-

flat(A, B-[]),

B = [T | L],

build(L, T, C).

Page 23: Prolog Programming (Volume 5)

Do it in one pass instead!Here is a better program:

normalise(X, Y) :- norm(X, []-Y).

norm(X+Y, A-C) :- !,

norm(X, A-B), norm(Y, B-C).

norm(X, []-X) :- !.

norm(X, A-(A+X)).

Here the accumulator is used not only for differencing the elements, but also for building the tree so far. The constant [] is used to represent the null accumulator, but there is no list processing as such.

Page 24: Prolog Programming (Volume 5)

Find maximum element of a treeA valued (weighted, coloured) binary tree can be defined using compound terms. A node is represented by n(V,L,R), where V is the value (say a number), and L and R are the left and right branches of the tree. A terminal node will have L and R instantiated to []. For example the term:n(3, n(1, n(4, [], []), n(1, [], [])), n(5, n(9, n(2, [], []), []), n(6, [], n(5, [], []))))

represents the following tree:

Page 25: Prolog Programming (Volume 5)

3

1

4 1

5

9

2

6

5

Notice no terminals coming from here and here.

Page 26: Prolog Programming (Volume 5)

Find maximum element of a treeJust like finding the maximum element of a list, use an accumulator to represent the ‘biggest value so far’./* findmax(Tree, Value so far, Final value) */

findmax([], A, A).findmax(n(V, L, R), A, Z) :-

V >= A, !,findmax(L, V, A1), findmax(R, A1, Z).

findmax(n(V, L, R), A, Z) :-V < A, !,findmax(L, A, A1), findmax(R, A1, Z).

Page 27: Prolog Programming (Volume 5)

Copy a treeMake a tree of the same shape as the input./* copyTree(InputTree, OutputTree) */

copyTree([], []).copyTree(n(V, L, R), n(V, L1, R1) :-

copyTree(L, L1),copyTree(R, R1).

Page 28: Prolog Programming (Volume 5)

Worksheet 32: Max TreeGiven an input tree T, write a program that constructs a tree of the same shape as T, but in which the value of each node has been set to the value of the greatest node in T. This can be by finding the greatest element, and then making a copy inserting the greatest element as the value of each node in the copy. However, it can be done in one pass (!) as follows:

Page 29: Prolog Programming (Volume 5)

Worked Exercise/* mt(InTree, OutTree, Hole, AccumHighest, Highest) */

maxtree(A, B) :- mt(A, B, H, 0, H).

mt(n(W, A, B), n(H, A1, B1), H, AC, N) :-W < AC,mt(A, A1, H, AC, ACA),mt(B, B1, H, ACA, N).

mt(n(W, A, B), n(H, A1, B1), H, AC, N) :-W < AC,mt(A, A1, H, AC, ACA),mt(B, B1, H, ACA, N).

mt([], [], H, A, A).

Page 30: Prolog Programming (Volume 5)

Difference Lists are...• Very efficient.• Popular -- in widespread use in Prolog.• Expressive of new methods of computation.• Sometimes misleading: difference-list append is

not free of side-effects (but is backtrackable)• Often hidden. Programmers encapsulate the

front and back in a structure p(L1, L2). Most common for this purpose is the infix ‘-’.