I'm new to Prolog and I'm trying write an in-order tree traversal where given a list of facts such as:
leftSubtree(9, 7). leftSubtree(7, 1). leftSubtree(1, -2). leftSubtree(11, 9). leftSubtree(16, 13). leftSubtree(3, 2). rightSubtree(9, 11). rightSubtree(7, 6). rightSubtree(1, 3). rightSubtree(11, 16). rightSubtree(16, 19).
I can use inOrder(9,X). to print out a the tree in order. I tried using the following code which works but was hoping for something simpler. Any tips or assistance would be appreciated.
inOrder(Root, X):- \+ leftSubtree(Root,Left), \+ rightSubtree(Root,Left) -> X = [Root]; leftSubtree(Root,Left), rightSubtree(Root,Right)-> inOrder(Left, LeftNode), inOrder(Right, RightNode), append(LeftNode,[Root|RightNode],X); leftSubtree(Root,Left), inOrder(Left, LeftNode), append(LeftNode,[Root],X); rightSubtree(Root,Right)-> inOrder(Right, RightNode), append([Root],RightNode,X).
preguntado el 01 de febrero de 12 a las 03:02
Your code can be simplified, avoiding negative tests, and using some of the reflexivity of Prolog.
inOrder(Root, [Left, Root, Right]) :- inOrder(leftSubtree, Root, Left), inOrder(rightSubtree, Root, Right). inOrder(BranchToExplore, Root, Subtree) :- call(BranchToExplore, Root, Branch) -> inOrder(Branch, Subtree) ; Subtree = .
This recover the estructura of the tree, as a nested ternary list, then a single call of flatten/2 suffices to make it, well, plano, as in your original requirement.
?- inOrder(8, Tree), flatten(Tree, L). Tree = [[[[, -2, ], 1, [[, 2, ], 3, ]], 5, [, 6, ]], 8, [[, 9, [, 10|...]], 11, [[, 13|...], 16, [...|...]]]], L = [-2, 1, 2, 3, 5, 6, 8, 9, 10|...].
If your Prolog doesn't have call/N, you can use univ para construir un invocable término:
inOrder(BranchToExplore, Root, Subtree) :- ( Callable =.. [BranchToExplore, Root, Branch], call(Callable) ) -> inOrder(Branch, Subtree) ; Subtree = .
If we swap Left and Root we have the more useful preOrder representation:
preOrder(Root, [Root, Left, Right]) :- ....
Having the structure of the tree in preOrder can be useful for further process: one 'ready to go' and useful implementation in SWI-Prolog it's XML, where trees are represented as element(Tag, Attributes, Subtrees), and can be handled by library(xpath). Could be an useful exercise change both inOrder/2 and inOrder/3 to recover XML.
inOrder(N,X) :- traverseLeft(N,L), traverseRight(N,R), append(L,[N|R],X). traverseLeft(N,L) :- leftSubtree(N,M) -> inOrder(M,L); L=. traverseRight(N,R) :- rightSubtree(N,M) -> inOrder(M,R); R=.
There's still a bit of room for improvement. append/3 is quite expensive, but you can avoid this by using accumulators.
The idea of an accumulator is that instead of writing a predicate
inOrder(N,X), meaning "the in-order traversal of the tree rooted at N is X", you write
inOrder(N,A,X), meaning "the in-order traversal of the tree rooted at N, appended to A, is X". Then you just kick it off by calling
The point of this is that you can get your recursive calls to build their solutions on top of your existing list, and avoid a linear-time list concatenation at every step.
inOrder(N,X) :- inOrder_aux(N,,X). inOrder_aux(N,A,X) :- traverseLeft(N,[N|R],X), traverseRight(N,A,R). traverseLeft(N,A,L) :- leftSubtree(N,M) -> inOrder_aux(M,A,L); L=A. traverseRight(N,A,R) :- rightSubtree(N,M) -> inOrder_aux(M,A,R); R=A.
So it's pretty much the same as the original solution, except that the base cases return the accumulator instead of the empty list, and you pass the result of the right traversal in as the accumulator of the left (before the right traversal has happened, as strange as that might seem).
Trees are better represented in Prolog by logical terms. A thorough discussion, including an answer to your problem can be found here (see solutions)