Thread View: comp.lang.prolog
2 messages
2 total messages
Started by Andrew Wenn
Mon, 07 Sep 1992 03:52
AVL trees - improving an implementation of:
Author: Andrew Wenn
Date: Mon, 07 Sep 1992 03:52
Date: Mon, 07 Sep 1992 03:52
162 lines
4460 bytes
4460 bytes
Dear Prolog Users,
I have implemented (almost entirely thanks to Ivan Bratko's book)
a program for AVL trees. However, it has a problem, which I
suspect is mainly due to the lack of tail recursion optimisation
in the prolog that I am using in that if I try to insert a large
number of items into it I run out of local stack space. For
various reasons, I cannot increase the size of the stack and
besides I feel this is the incorrect approach. By the way if I
use a simple binary dictionary, I can insert all the records I
wish into it.
Before you ask, this is not an assignment!
I have taught myself all the prolog that I know but I feel that
it may be beneficial to myself and others in a similar situation
if we could discuss various approaches for improving the program
here.
There are some quite talented and experienced prolog users
who read this news; let's hope they rise to the challenge of
passing on their knowledge to the less experienced ones.
For those who are unfamiliar with Bratko's book, the code for avl
trees is attached below.
cut here
----------------------------------------------------------
%%% A program for constructing and searching an avl tree.
/* Based on Bratko pp 244ff. */
/* Build the tree. */
% The root of the tree is Key.
addavl( nil/0, Key, avl(nil/0, Key, nil/0)/1 ).
addavl( avl(Left, Y, Right)/Hy, Key, NewTree):-
eq(Y, Key),
!,
NewTree = avl(Left, Y, Right)/Hy.
addavl( avl(Left, Y, Right)/Hy, Key, NewTree):-
gt(Y, Key),
addavl(Left, Key, avl(Left1, Z, Left2)/_ ),
combine(Left1, Z, Left2, Y, Right, NewTree).
addavl( avl(Left, Y, Right)/Hy, Key, NewTree):-
gt(Key, Y),
addavl(Right, Key, avl(Right1, Z, Right2)/_ ),
combine(Left, Y, Right1, Z, Right2, NewTree).
combine(T1/H1, A, avl(T21, B, T22)/H2 , C, T3/H3,
avl(avl(T1/H1, A, T21)/Ha, B, avl(T22, C, T3/H3)/Hc)/Hb ):-
H2 > H1,
H2 > H3,
Ha is H1 + 1,
Hc is H3 + 1,
Hb is Ha + 1.
combine(T1/H1, A, T2/H2, C, T3/H3,
avl(T1/H1, A, avl(T2/H2, C, T3/H3)/Hc)/Ha ):-
H1 >= H2,
H1 >= H3,
max1(H2, H3, Hc),
max1(H1, Hc, Ha).
combine(T1/H1, A, T2/H2, C, T3/H3,
avl(avl(T1/H1, A, T2/H2)/Ha, C, T3/H3)/Hc ):-
H3 >= H2,
H3 >= H1,
max1(H1, H2, Ha),
max1(Ha, H3, Hc).
max1(U, V, Max):-
( U > V,
!,
Max is U + 1
; Max is V + 1
).
/* Display a tree. */
show(T):-
show2(T,0).
show2(nil/0,_).
show2(avl(Left, X, Right)/_, Indent):-
Ind2 is Indent + 2,
show2(Right, Ind2),
tab(Indent),
write(X),
nl,
show2(Left, Ind2).
/* Find an item in a binary tree. */
intree(X):-
clause(tree(T), true),
in(X, T).
in(X, avl(_,X,_)/_ ):-!.
in(X, avl(Left, Root, _)/_ ):-
gt(Root, X),
in(X, Left).
in(X, avl(_, Root, Right)/_ ):-
gt(X, Root),
in(X, Right).
build(File):-
see(File),
write('Loading dictionary.\n'),
build_tree(Tree),
seen,
write('Complete.\n'),
show(Tree).
build_tree(Final):-
build_tree_next(nil/0, Final).
build_tree_next(Tree, NewTree):-
read(Item),
( Item = end_of_file -> true,
NewTree = Tree
; addavl(Tree, Item, Tree1),
build_tree_next(Tree1, NewTree),
!
).
/* Test to see where item is to be put. */
gt(X, Y):-
X @> Y.
eq(X, Y):-
X == Y,
!,
write(X),
write(' Item already in tree\n').
--------------------------------------------------------------
A sample (the whole file has about 500 of them) of the data that
I am testing my implementation with are facts with the following
format:
cut here
------------------------------------------------------------------
item(ant).
item(a).
item(zak).
item(john).
item(queen).
item(harry).
item(harvey).
item(jane).
item(james).
item(robert).
item(zoo).
item(felicity).
item(queen0).
item(ant1).
Re: AVL trees - improving an implementation of:
Author: Fergus James HENDERSON
Date: Tue, 08 Sep 1992 07:12
Date: Tue, 08 Sep 1992 07:12
100 lines
4655 bytes
4655 bytes
awenn@matilda.vut.edu.au (Andrew Wenn) writes:
>I have implemented (almost entirely thanks to Ivan Bratko's book)
>a program for AVL trees. However, it has a problem, which I
>suspect is mainly due to the lack of tail recursion optimisation
>in the prolog that I am using in that if I try to insert a large
>number of items into it I run out of local stack space. For
>various reasons, I cannot increase the size of the stack and
>besides I feel this is the incorrect approach. By the way if I
>use a simple binary dictionary, I can insert all the records I
>wish into it.
A good reference which covers this sort of thing is "The Craft of Prolog"
by Richard O'Keefe, in particular chapter 3, "Where Does All The Space Go?".
>----------------------------------------------------------
>%%% A program for constructing and searching an avl tree.
>
>/* Based on Bratko pp 244ff. */
>
>/* Build the tree. */
>
>% The root of the tree is Key.
>
>addavl( nil/0, Key, avl(nil/0, Key, nil/0)/1 ).
Using '/' in this way is convenient, but most Prolog compilers
are not smart enough to avoid using extra space if you do so.
The following is likely to be more space-efficient:
addavl( nil(0), Key, avl(nil(0), Key, nil(0), 1) ).
Ie. replace nil/H with nil(H) and avl(L,K,R)/H with avl(L,K,R,H).
[You will need to change all the rest of the code too :-( ]
>addavl( avl(Left, Y, Right)/Hy, Key, NewTree):-
> eq(Y, Key),
> !,
> NewTree = avl(Left, Y, Right)/Hy.
>
>addavl( avl(Left, Y, Right)/Hy, Key, NewTree):-
> gt(Y, Key),
> addavl(Left, Key, avl(Left1, Z, Left2)/_ ),
> combine(Left1, Z, Left2, Y, Right, NewTree).
>
>addavl( avl(Left, Y, Right)/Hy, Key, NewTree):-
> gt(Key, Y),
> addavl(Right, Key, avl(Right1, Z, Right2)/_ ),
> combine(Left, Y, Right1, Z, Right2, NewTree).
The basic problem is likely to be that although the above code
is deterministic, most Prolog compilers are not going to be smart
enough to detect this, and you are going to get unnecessary choice
points on the stack. You could improve things by putting a cut after the
call gt(Y, Key), which would remove the choice point from the stack,
but it is better to avoid pushing the choice point in the first place.
Most Prolog compilers "index" clauses on the top-level functor of the
first argument, and will detect determinism if the top-level functors
of the first argument of each clause are different (and avoid pushing
choice points). Thus the following code may be much more efficient:
% top-lvl functors of 1st arg are nil/1 and avl/4.
addavl( nil(0), Key, avl(nil(0), Key, nil(0), 1)).
addavl( avl(Left, Y, Right, Hy), Key, NewTree):-
termCompare(Y, Key, Result),
addavl_2(Result, Left, Y, Right, Hy, Key, NewTree).
% Using termCompare/3 above allows us to index on 1st arg here,
% thus avoiding creating choice points
addavl_2(=, Left, Y, Right, Hy, _Key, avl(Left, Y, Right, Hy)):-
write(Y), % don't hide these writes in eq/2 - put them
% here in addavl so they're obvious.
% eq/2 might be used for other purposes.
write(' Item already in tree\n').
addavl_2(>, Left, Y, Right, Hy, Key, NewTree):-
addavl(Left, Key, avl(Left1, Z, Left2, _),
combine(Left1, Z, Left2, Y, Right, NewTree).
addavl_2(<, Left, Y, Right, Hy, Key, NewTree):-
addavl(Right, Key, avl(Right1, Z, Right2, _),
combine(Left, Y, Right1, Z, Right2, NewTree).
You should use the same trick with termCompare and an auxiliary predicate
for searching the AVL tree.
>combine(T1/H1, A, avl(T21, B, T22)/H2 , C, T3/H3,
> avl(avl(T1/H1, A, T21)/Ha, B, avl(T22, C, T3/H3)/Hc)/Hb ):-
As far as I know, most Prolog compilers don't seem to do common subexpression
elimination, so repeating the terms T1/H1 and T3/H3 will cost you both time
and heap space. (Another reason to avoid using '/'/2 in this way.)
Hope all this helps,
Fergus.
--
Fergus Henderson fjh@munta.cs.mu.OZ.AU
This .signature virus is a self-referential statement that is true - but
you will only be able to consistently believe it if you copy it to your own
.signature file!
Thread Navigation
This is a paginated view of messages in the thread with full content displayed inline.
Messages are displayed in chronological order, with the original post highlighted in green.
Use pagination controls to navigate through all messages in large threads.
Back to All Threads