Title: Prolog Programming (Volume 5)
1Prolog Programming(Volume 5)
2List 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 HT 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
3List 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)
4Difference Structures
- Probably 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.
5Concatenating two lists (WS 25)
- ?- append(a, b, c, d, e, f, X).
- X a, b, c, d, e, f
- append(, L, L).
- append(XY, T, XZ) - 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.
6Concatenating 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
7Linearising (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(HT, L3) -
- flatten(H, L1), flatten(T, L2), append(L1, L2,
L3). - flatten(X, X).
- This is very inefficient how many calls to
append?
8Instead, Difference Lists
- The 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
L1 a, b L2
a
b
9Example Append L1-L2 to L3-L4
L1
L2
L3
L4
a
d
b
e
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)
10Make it so
L1
L2
L3
L4
X
Y
a
d
b
e
c
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.
11Definition of app
- The 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).
12Usual notation of difference lists
- Denote 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!
13Summary
- L-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.
14Linearising (flattening) a list (WS 28)
- flatten(X, Y) - flatpair(X, Y-).
- flatpair(, L-L).
- flatpair(HT, L1-L3) -
- flatpair(H, L1-L2), flatpair(T, L2-L3).
- flatpair(X, XZ-Z).
- Notice the pattern of stitching elements
together.
15Linearising (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
16Linearising (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, ).
17Linearising (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, ).
18Worked 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).
19Normalising Sum Expressions (WS 30)
The sums (ab)(cd) and (a(b(cd))) may be
normalised into a standard form that associates
on the left abcd, or equivalently,
((ab)c)d. This is only possible because they
are sums.
d
a
c
b
a
b
c
d
d
c
b
a
20Define a predicate normsum
The 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(XY), R1-R3) - !,
flat(X, R1-R2), flat(Y, R2-R3). flat(X, XZ -
Z). The difference thread is R1?R2?R3, and note
the ! to commit to the first clause when a sum
node is encountered.
21Building up the normalised sum
This 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, TX)
- !. build(H L, T, Z) - build(L, TH, Z).
22Putting them together
Now 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).
23Do it in one pass instead!
Here is a better program normalise(X, Y) -
norm(X, -Y). norm(XY, A-C) - !, norm(X,
A-B), norm(Y, B-C). norm(X, -X) - !. norm(X,
A-(AX)). 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.
24Find maximum element of a tree
A 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
253
1
5
6
9
1
4
2
5
Notice no terminals coming from here and here.
26Find maximum element of a tree
Just 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 gt A, !, findmax(L, V, A1),
findmax(R, A1, Z).findmax(n(V, L, R), A, Z)
- V lt A, !, findmax(L, A, A1), findmax(R, A1,
Z).
27Copy a tree
Make 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).
28Worksheet 32 Max Tree
Given 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
29Worked 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
lt 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
lt AC, mt(A, A1, H, AC, ACA), mt(B, B1, H, ACA,
N). mt(, , H, A, A).
30Difference 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 -.