prologbinary-search-treedcgfailure-slice

Enumerate inorder in Prolog


I'm trying to "reverse" an inorder traversal by turning the inorder list back into BSTs.

BSTs has the predicate form node(Value,Left,Right) or leaf, so the empty tree is just leaf, a tree with one node is node(_,leaf,leaf).

Given the predicate enumerate_bst(Elems,Bst), the goal is to match Bst to all possibilities of the inorder list Elems. For example (note setof/3):

?- setof(Bst,enumerate_bst([],Bst),Enum).
Enum = [leaf].

?- setof(Bst,enumerate_bst([1,2],Bst),Enum).
Enum = [
    node(1, leaf, node(2, leaf, leaf)),
    node(2, node(1, leaf, leaf), leaf)
].

?- setof(Bst,enumerate_bst([1,2,3],Bst),Enum).
Enum = [
    node(1, leaf, node(2, leaf, node(3, leaf, leaf))),
    node(1, leaf, node(3, node(2, leaf, leaf), leaf)),
    node(2, node(1, leaf, leaf), node(3, leaf, leaf)),
    node(3, node(1, leaf, node(2, leaf, leaf)), leaf),
    node(3, node(2, node(1, leaf, leaf), leaf), leaf)
].

What I tried to do:

I already have a predicate that matches a given BST to it's inorder list:

inorder(leaf,[]).
inorder(node(X,L,R),Elems) :-
  inorder(L,Elems_L),
  inorder(R,Elems_R),
  append(Elems_L,[X],Tmp),
  append(Tmp,Elems_R,Elems).

I tried to use it in reverse by putting in a list instead, but this only returned one result, I'm not exactly sure why.

What I'm currently trying to do

I'm trying to use the native predicate append/3 in reverse, but can't fully decide how this will be done.

Any ideas as to the best way to do this? Thanks!


Solution

  • In reverse, your program does not terminate. Consider:

    ?- inorder(Tree, []).
       Tree = leaf
    ;  loops.
    

    We would expect that it just shows this only solution and then stops. The actual reason is the following fragment of your program - a . No need to look any further than that. In other words, no need to understand append/3 at all.

    ?- inorder(Tree, []), false.
    
    inorder(leaf,[]) :- false.
    inorder(node(X,L,R),Elems) :-
       inorder(L,Elems_L), false.
       inorder(R,Elems_R),
       append(Elems_L,[X],Tmp),
       append(Tmp,Elems_R,Elems).
    

    First, this fragment needs to terminate (and fail). Only then you may consider termination of the entire program. Within this fragment, the second argument (Elems) is completely ignored! The first goal that would be interested in it is the very last one (append(Tmp,Elems_R,Elems)).

    A naive way out would be to reorder the goals, putting the two append/3 goals first. That works (that is, it terminates for a ground second argument), but it is quite unsatisfying, as the program is now devoted to distributing elements of a list into trees only.

    Here is a version that works both ways as indicated by the termination condition:

    inorder(A,B)terminates_if b(A);b(B).
    

    which states that inorder/2 terminates if the first or the second argument is ground.

    inorder(Tree, Xs) :-
       phrase(inorder(Tree, Xs,[]), Xs).
    
    inorder(leaf, Vs,Vs) -->
       [].
    inorder(node(X,L,R), [_|Vs0],Vs) -->
       inorder(L, Vs0,Vs1),
       [X],
       inorder(R, Vs1,Vs).