Tree/list constraints
Tree constraints allow you to define relations over "structured" types, which vaguely resemble Miranda constructors; Here's a "complex number" type in action:
c_add( c(R1,I1), c(R2,I2), c(RSum,ISum)) :- RSum = R1 + R2, ISum = I1 + I2. Notice that:
- We do not need a separate declaration for the c(R,I) relation. It is implicitly defined in the definition of c_add.
- c_add is a relation among three tuples of the tree type c(X,Y), and therefore there is no notion of producing a result. Instead, the relation simply is satisfiable if the body of the rule is satisfiable.
1 ?- c_add(c(1,1),c(2,2),D). D = c(3, 3) *** Yes 2 ?- c_add(E, c(1,3), c(5,X)). E = c(4, X - 3) *** Yes You can also define recursive types using tree constraints:
find( node(_,I,_), E ) :- E = I. /* Binary search tree */ find( node(L,I,_), E ) :- E < I, find(L,E). find( node(_,I,R), E ) :- E > I, find(R,E). Notice the use of the special variable _ (underscore), which represents a wholly unconstrained value (each occurrence of _ is given an implicit, unique subscript). List constraints are simply a special kind of tree constraint:
[X|[2]] = [1,2]. /* Use of list primitives */ cons(X,cons(2,nil)) = cons(1,cons(2,nil)). /* Equivalent goal */ car(cons(A,B), X) :- A = X. /* Tree rules for car/cdr */ cdr(cons(A,B), X) :- B = X.