-1

I am studying Prolog and I find it difficult to implement a predicate that takes a list and builds a balanced tree from it.

I have implemented these predicates that build an AVL Tree (I have taken it from Bratko book and it works fine):

%%%  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
    ). 

eq(X, Y):-
    X == Y,
    !,
    write(X),
    write(' Item already in tree\n'). 

So I have the addavl(Tree, Element, NewTree/Height) predicate that adds the new element to a Tree generating a new AVL tree.

Now I would like to create a new predicate that uses this addavl/3 predicate to create a new AVL tree from a list of elements.

For example, if I have the list: [1,2,3], this new predicate creates a new AVL tree that contains the elements 1,2,3.

I am trying to do this but I am finding some difficulties to do it.

I have implemented something like this (but it doesn't work):

%% BASE CASE: The list is empty, so there is no element to insert 
%%            into the AVL Tree: 

buildTreeList(Tree, [], NewTree, Height) :- !.


%% If I am inserting the first element in the AVL Tree 
%% the hight H of the Tree after the insertion is 1: 

buildTreeList(Tree, [Head|Tail], NewTree, 1) :-  
    addavl(nil/0, Head, avl(nil/0, Head, nil/0)/H),
    Tree = nil/0,
    NewTree = avl(nil/0, Head, nil/0)/1,
    buildTreeList(NewTree, Tail, NT, Height).

buildTreeList(Tree, [Head|Tail], NewTree, H) :- 
    addavl(Tree, Head, avl(Tree, Head, NewTree)/H),
    buildTreeList(NewTree, Tail, NT, Height). 

My idea is: the addavl/3 predicate adds an element to a new AVL tree and gives me also the height of this new tree (because I have the couple NewTree/Height).

So my idea is to:

  1. Scroll through the list of items until the empty list (the base case: there is no items in the list so I don't insert anything into the AVL Tree)

  2. Insert any element of the list into the AVL tree.

  3. If the AVL Tree is empty it has height=0 so I am creating a new AVL Tree by:

     addavl(nil/0, Head, avl(nil/0, Head, nil/0)/H)
    
  4. If the AVL Tree is not empty I insert into it.

But it doesn't work and probably is the wrong way to do this thing.

Could someone help me?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
AndreaNobili
  • 40,955
  • 107
  • 324
  • 596

1 Answers1

2

You are trying to re-implement maplist/N.

You have addavl(Tree, Element, NewTree). The trees are already in the form T/Height. You must start with nil/0.

buildTree(List,Tree):-
  length(List, N), 
  length(L1, N), append(L1, [Tree], [nil/0 | L2]),
  maplist( addavl, L1, List, L2).

(not tested).

The point is, use addavl/3 as a given, opaque predicate, don't revisit its definition.

The sharing of logical variables between the two lists L1 and L2 (shifted by one position) serves to arrange for the passing of accumulated result from one step of the calculation into the next, stating with nil/0, until the full tree Tree is built in the final step. This is trading efficiency for convenience. You should re-implement this in direct recursive style, especially if the list of elements is expected to be long.


a note: whether to use maplist or hand-roll a direct recursive solution, is a syntactical issue. Both variants describe same iterative computational process of progressively adding elements into a tree by calling addavl, using the output of previous call as input to the next. A common pattern, which e.g. in Haskell, coincidentally, is captured with a higher-order procedure named - surprise! - iterate.

(that's only true on a higher-level of course. In the concrete implementation, like SWI Prolog, one can be optimized far better than the other. Using lists, here, will be probably less efficient than the other variant).

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • ok, but this is an exercise give from the teacher that explicitly ask to implement these predicates using the previous AVL Tree implementation... – AndreaNobili Apr 29 '13 at 17:58
  • and what is the `addavl` predicate that I'm using? :) – Will Ness Apr 29 '13 at 17:59
  • Ok, seems work...now I will try to understand it to myself and if I have problem I come back here to ask you ;-) – AndreaNobili Apr 29 '13 at 18:01
  • success and good tidings! :) this is a Haskell `zipWith` trick that I'm using, BTW. – Will Ness Apr 29 '13 at 18:02
  • what do you mean this is Haskell ?!?! Haskell is not another language different from Prolog? (I am very new in declarative word) – AndreaNobili Apr 29 '13 at 18:11
  • 1
    Haskell is a different language of course; if you're not familiar with my reference, just ignore it. :) There is some similarity in list handling between Haskell and Prolog, and this trick can be used in Haskell as here, in Prolog. – Will Ness Apr 29 '13 at 18:12
  • And about "declarativeness" that you evoke so often, it is a Real World that we live in, and these are the Real World Tools that we use. The Holy Grail has not yet been found, and the programming languages that we use are not yet clear, pristine, declarative, angelic things. Yet. :) :) Me, I'm expecting the arrival of English (TM) (R) compiler any day, but it's not here yet. :) – Will Ness Apr 29 '13 at 18:15
  • mmmm I am trying to understand your previous code snippet...can you try to give me some more explaination about it? please :-) – AndreaNobili Apr 29 '13 at 19:01
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/29134/discussion-between-will-ness-and-andreanobili) – Will Ness Apr 29 '13 at 19:01