How can terms be used to construct data structures? A first step is
observing that terms can be as a whole bound to variables, and thus
they can be passed to predicates as arguments. For example, a fact in
a database of
teacher, subjects, hours and classes, could be written as follows:
course(comp_logic, mond, 19, 21, 'Manuel', 'Hermenegildo',
A call to this predicate is:
?- course(comp_logic, Day, Start, End, C, D, E, F).
in which we are only really interested in the day, start, and end hours of the course. Some arguments can be easily put together to make up more structured data: for example, name and surname, date, location...The clause can then be rearranged as follows:
course(comp_logic, Time, Lecturer, Location):- Time = time(mond, 19, 21), Lecturer = lecturer('Manuel', 'Hermenegildo'), Location = location(new, 1302).
The constraints have been taken out of the head (remember
Section 2.1.3) for the sake of clarity. A query to find out
the date, start, and end of the lecture, would be:
?- course(comp_logic, Time, _, _).
(using the anonymous variable ``_'' to denote variables
whose value we are not interested in, and thus they are not displayed
at all) and the answer:
Time = time(mond, 19, 21)
The previous examples use terms to implement records. This is one of the main (but not the only) ways of using terms to build data structures. We will develop a larger example of using terms to structure and hide data. We will start with a facts database about people and friendship relations among them:
friends(peter, mark). friends(anna, marcia). friends(anna, luca).
Some queries to this program be:
?- friends(anna, X). X = marcia ; X = luca ?- friends(X, anna). no
The last answer is correct: although we intuitively think that if Anna and, say, Marcia are friends, then Anna is a friend of Marcia and Marcia is a friend of Anna, the program has no way of knowing this unless explicitly told--argument position matters. So we have to write another predicate which implements the symmetry of the friendship:
are_friends(A, B):- friends(A, B). are_friends(A, B):- friends(B, A).
Note that there are no constants in this predicate: only variable passing. Everything works as expected now:
?- are_friends(anna, X). X = marcia ; X = luca ?- are_friends(X, anna). X = marcia ; X = luca
Some of the people in the friends database are married:
married(couple(peter, anna)). married(couple(mark, kathleen)). married(couple(alvin, marcia)).
Note that we are putting together the couple in a data structure: married/1 actually defines couples of persons:
?- married(A). A = couple(peter,anna) ; A = couple(mark,kathleen) ; A = couple(alvin,marcia)
Then, as before, we might want to know who is married to who:
?- married(couple(peter, S)). S = anna ?- married(couple(marcia, S)). no
And we have a similar problem: couple/2 also keeps an order on the marriage. A possible solution is using a predicate which constructs / deconstructs couples:
spouse(couple(A, B), A). spouse(couple(A, B), B).
which says that ``A is one of the spouses in the couple formed by A and B, and B is also one of the spouses in the same couple.''. Then we can ask for marriages given only one of the spouses, and regardless of the order in which it appears in the definition of the couples:
?- spouse(C, peter), married(C). C = couple(peter,anna) ?- married(C), spouse(C, marcia). C = couple(alvin,marcia) ?- spouse(C, luca), married(C). no
Last, we will define conditions for going out to have dinner: two couples will have dinner together if spouses in the two couples are friends:
go_out_for_dinner(Ma, Mb):- married(Ma), married(Mb), spouse(Ma, A), spouse(Mb, B), are_friends(A, B). ?- go_out_for_dinner(A, B). A=couple(peter,anna), B=couple(mark,kathleen) ; A=couple(peter,anna), B=couple(alvin,marcia) ; A=couple(mark,kathleen), B=couple(peter,anna) ; A=couple(alvin,marcia), B=couple(peter,anna)