2

I'm a beginner in Prolog, and my first assignment is to implement a function construct(), that builds a binary tree from a list. I know there is something wrong or missing in my code, but I can't put my finger on it. I also think a helper method may be necessary, but I can't think of how to go about that. Here is my code so far:

construct([],nil).
construct(E, tree(E,nil,nil)).
construct([H|T], tree(H, construct(T,R), nil)):- 
          T>H.
construct([H|T], tree(H, construct(T,R), nil)):- 
          T<H.
3
  • 1
    Do you need to create a binary tree or a binary search tree? Commented Nov 8, 2021 at 1:09
  • The single most important question that you don't answer here is what kind of tree you expect. Check out this reddit thread, make sure you read the question and all comments (there is only 3 of them so not too much work I hope). Maybe this will bring you closer to asking a good question. Commented Nov 8, 2021 at 7:03
  • PS: if you just took the code from reddit I linked above and wrote: sort(List, Sorted), list_to_tree(Sorted, Tree) then you will have a balanced binary search tree in Tree with all the unique elements that were in List. You need to make them unique otherwise it will not be a binary search tree. But this already assumes a lot of things based on insufficient information in your question. Commented Nov 8, 2021 at 8:06

2 Answers 2

2

In Prolog, we can use:

  • the atom nil to represent an empty binary tree, and
  • a term of the form t(Left, Root, Right) to represent a non-empty binary tree, where Root is the value at the root of the tree, and Left and Right are also terms representing binary trees.

To make the binary trees easier to see, you can use the following predicate:

show(T) :-
    show(T, 0).

show(nil, _).
show(t(Left, Root, Right), Indent) :-
    Indent1 is Indent + 3,
    show(Right, Indent1),
    format('~*c~w\n', [Indent, 32, Root]),
    show(Left, Indent1).

Example:

?- show( t(t(nil,1,nil), 2, t(nil,3,nil)) ).
   3
2
   1
true.

Let List be a list denoting the in-order traversal of an arbitrary binary tree, as illustrated bellow:

enter image description here

Then, since different binary trees can have the same in-order traversal, a binary tree corresponding to such list can be described as follows:

% convert from in-order traversal list to binary tree

list_to_bt(List, Tree) :-
    (   List = []
    ->  Tree = nil
    ;   Tree = t(Left, Root, Right),
        append(Prefix, [Root|Suffix], List),
        list_to_bt(Prefix, Left),
        list_to_bt(Suffix, Right) ).

Example:

?- list_to_bt([1,2,3], T), show(T).
      3
   2
1
T = t(nil, 1, t(nil, 2, t(nil, 3, nil))) ;
   3
      2
1
T = t(nil, 1, t(t(nil, 2, nil), 3, nil)) ;
   3
2
   1
T = t(t(nil, 1, nil), 2, t(nil, 3, nil)) 
...
false.

If you want to obtain only balanced binary trees (e.g., trees such that, for each node, the absolute difference in the sizes of the right and left subtrees is at most 1), then you can include this constraint as follows:

% convert from in-order traversal list to balanced binary tree (bbt)

list_to_bbt(List, Tree) :-
    (   List = []
    ->  Tree = nil
    ;   Tree = t(Left, Root, Right),
        append(Prefix, [Root|Suffix], List),
        length(Prefix, Size1),
        length(Suffix, Size2),
        abs(Size1 - Size2) =< 1,
        list_to_bbt(Prefix, Left),
        list_to_bbt(Suffix, Right) ).

Example:

?- list_to_bbt([1,2,3], T), show(T).
   3
2
   1
T = t(t(nil, 1, nil), 2, t(nil, 3, nil)) ;
false.

If you want only balanced binary search trees from an arbitrary list, then you must sort this list before creating the balanced binary tree:

% convert from arbitrary list to balanced binary search tree (bbst)

list_to_bbst(List, Tree) :-
    sort(List, Sorted),
    list_to_bbt(Sorted, Tree).

Examples:

?- list_to_bbst([3,1,7,5,4,2,6], T), show(T).
      7
   6
      5
4
      3
   2
      1
T = t(t(t(nil, 1, nil), 2, t(nil, 3, nil)), 4, t(t(nil, 5, nil), 6, t(nil, 7, nil))) ;
false.

?- list_to_bbst([3,1,4,2], T), show(T).
      4
   3
2
   1
T = t(t(nil, 1, nil), 2, t(nil, 3, t(nil, 4, nil))) ;
   4
      3
2
   1
T = t(t(nil, 1, nil), 2, t(t(nil, 3, nil), 4, nil)) ;
   4
3
      2
   1
T = t(t(nil, 1, t(nil, 2, nil)), 3, t(nil, 4, nil)) ;
   4
3
   2
      1
T = t(t(t(nil, 1, nil), 2, nil), 3, t(nil, 4, nil)) ;
false.
Sign up to request clarification or add additional context in comments.

Comments

1

First: there are no functions in Prolog, just predicates. So, if you write tree(H, construct(T,R), nil) (from your 2nd clause), then you've created a structure (or "term") that looks like this:

            tree
         /   |     \
       /     |       \
       H  construct  nil
           /   \
          /     \
         T       R

which is not what you want.

Instead, you should write something like this (although this won't do what you want either -- see below):

construct([H|T], tree(H, Tree, nil)):- 
    T>H,
    construct(T, R, Tree).

Second: you should have an easy way to distinguish a value from a non-value. You've chosen nil for non-value, so you could have something like value(V) for storing a value.

Now, think about what a node looks like. It's got 2 parts (left and right), each of which can be either nil, a value or a node. For example, you'd want [1,2,3] to produce a tree that looks something like

node(node(value(1),
          value(2)),
     value(3))

As for an auxiliary predicate (not "function"), an obvious one is a predicate that inserts a single value to a tree. So, just list out all the possibilities:

insert_value(X, node(nil, nil), node(value(X), nil)).  % insert into an empty node
insert_value(X, node(value(Y), nil), node(value(X), value(Y))) :- X < Y.
insert_value(X, node(value(Y), nil), node(value(Y), value(X))) :- X > Y.
insert_value(X, node(node(A,B), value(Y)), node(T1, value(Y))) :-
    X < Y,
    insert_value(X, node(A,B), T1).

etc.

If you do this, you'll find that this isn't the best representation for a tree, but I'll leave it to you to come up with a better representation. (Incidentally, there's no need for all the node functors to have 2 elements; it's ok to have node - contains nothing; node(X) - contains a single item; node(X,Y) - contains 2 items).

Also, you'll need to decide what to do wwhen inserting a value that's already in the tree -- does insert_value/3 fail, does it just do nothing, or does it allow multiple identical values?

Once you've got this working, then you can write a predicate to insert a list of values:

insert_values([], Tree, Tree).
insert_values([X|Xs], Tree0, Tree) :-
    insert_value(X, Tree0, Tree1),
    insert_values(Xs, Tree1, Tree).

This is a very common pattern; the general form of it is called "foldl".

And finally, get the whole thing going with an initial value of an empty node:


insert_values(Values, Tree) :-
    insert_values(Values, node(nil,nil), Tree).

It's also very common in Prolog to define predicates with different numbers of arguments; in this case, insert_values/2 uses insert_values/3 with an initial value of an empty node (node(nil,nil)).

2 Comments

This is a matter of style, but creating predicates (or even compound terms) with the same name and different number of arguments can be a source of mistakes. The only place where I would recommend doing it is in the "public interface" of a library, to provide default values for arguments. In this particular case you could also make an argument for making node/1, node/2, node/3 compound terms but I am slightly conflicted about it.
I often use predicates with different numbers of arguments (or compound terms with different arities), and that hasn't been a significant source of bugs for me. However, it's good to not overdo this -- the it really should be used only where there are "optional" arguments but the semantics are otherwise essentially the same. As @TA_intern says, it's somewhat a matter of taste and experience.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.