University of Kent
male(john).
In this example male (read `is male') is the predicate, and john is the subject. The English interpretation of this statment (a Prolog `fact') is john is male or the set of males includes john . Prolog will interpret this statement in different ways depending the context of its entry by the user. There are two basic contexts in Prolog, consult context and query context.10 will be taken as an asserted fact by the user, and Prolog will record this fact in its database. In query context Prolog will test the fact against its database (a collection of previously asserted facts). In these examples query context will be denoted by the presence of the characters `?-' preceding the clause:
?- male(john).
yes
Note that Prolog responds yes if the clause is proven (true) relative to its database. The terms male and john are spelled beginning with lowercase letters. This is required in Prolog, as words beginning with capital letters are reserved for an important function. If the following is typed:
?- male(X).
X = john
The use of capital ` X ' in the assertion indicates an unnamed subject, and is called a variable. Any word beginning with a capital letter would have done. A term beginning with a capital letter has a function similar to a pronoun, and Prolog responds to such an assertion by giving values for that term that makes the assertion male(X) true. If there is more than one such value, Prolog will give each of them on request. This request is made by typing a `;' after the reply from Prolog. If the following facts are added to Prolog's database:
male(tom).
male(mike).
and Prolog is given the query:
?- \fBmale(X).
\fIX = john ;
X = tom ;
X = mike ;
no
The no indicates that there are no more solutions to the assertion male(X) . Prolog works on a principle of accepting `facts' from the user, and testing assertions by evaluation based either on the presence of the fact in the database if a fact is specified, or by finding a fact in the database if a fact is not specified. Prolog also handles more complex facts:
child(mike,tom)
child(john,tom).
These facts inform Prolog that mike and tom have the relation child between them. Prolog does not know (and does not care!) who is child of whom, so it is important to be consistent with respect to the order the arguments are given. In this paper these relationships will conventionally be read `the first argument has the relation to the second argument', thus mike is the child of tom, and john is the child of tom. The new facts are tested in the following queries:
?- \fBchild(mike,tom).
\fIyes
?- \fBchild(tom,mike).
\fIno
?- \fBchild(Kid,tom).
\fIKid = mike ;
Kid = john ;
no
?- child(Kid,Pater).
Pater = tom
Kid = mike ;
Pater = tom
Kid = john ;
no
Although the above might have some value as a computer based tool for the anthropologist, the property of Prolog that makes it potentially a very powerful research tool is its ability to understand and evaluate rules that are supplied by the user:
parent(X, Y) :- child(Y, X).
father(X, Y) :- parent(X, Y) , male(X).
mother(X, Y) :- parent(X, Y) , female(X).
The preceding rules indicate that X is the parent of Y when Y is the child of X, X is father of Y when X is parent of Y and X is male, and X is mother of Y when X is parent of Y and X is female. A rule is a way of generating new facts based on previous facts and rules. The father-rule or mother-rule can be used just like facts in further rules, and will, in general, act just as if it were a fact:
?- father(tom,john).
yes
?- father(tom,Kid).
Kid = mike ;
Kid = john ;
no
The facility to build complex relationships in Prolog using rules allows experimentation with constructing relationships based on prior relationships. For example the following fact and rule can be added to the database:
child(tom,carl).
grandfather(X,Y) :- father(X,Q) , father(Q,Y).
The basic information that tom is child of carl is added, and then the rule that states that X is grandfather of Y if X is the father of Q, and Q is the father of Y. This new rule can be interrogated:
?- \fBgrandfather(Gf,john).
\fIX = carl ;
no
Note that no value for Q in the grandfather rule was printed. Prolog only prints values for pronominal objects (or variables in Prologese) that appear at the top level, the level of the query. Variables used only inside rules are only for internal use of that rule. Note also that the variable used in the rule definition need not be the same as the one used in the query. The only requirement is that each variable used in a query has a unique form, unless it is intended that the same value be used, as in the case of the Q in the grandfather rule. In terms of extending the genealogical database, rules could be constructed for each relationship of interest:
sibling(X,Y) :- parent(Q,X) , parent(Q,Y), X \= Y.
(X is (half-)sibling of Y if the parent of X (Q) is the parent of Y, and X is not the
same person as Y!. This could be restricted to X and Y sharing two parents by
the rule...sib(X,Y):-father(Q,X),father(Q,Y),mother(R,X),mother(R,Y),X\=Y.)
brother(X,Y) :- sibling(X,Y) , male(X).
(X is brother of Y if X is sibling of Y and X is male.)
grand-parent(X,Y) :- parent(X,Q) , parent(Q,Y).
(X is the grand-parent of Y if X is parent of parent of Y)
cousin(X,Y) :- parent(Q,Y) , sibling(Z,Q) , child(X,Z).
cousin(X,Y) :- grand-parent(Q,Y) , sibling(Z,Q) , child(X,Z).
cousin(X,Y) :- grand-parent(Q,Y) , sibling(Z,Q) , grand-parent(Z,X).
cousin(X,Y) :- parent(Q,Y) , sibling(Z,Q) , grand-parent(Z,Y).
(Four rules for cousin (out of others)... Prolog will try each until one
is true relative to the database).
This process continues until suitable definitions were produced for each kin category desired. Note that although the present definitions all hinge on the primitive relation child-of , the primitive criteria could be some other relationship. Less naive rules could also be implemented, or rules for categories which are dependent on the sex of the linking relative. Affinal relations can be added by including a spouse-of relation or rule (as deemed appropriate). By filling out the rule set, a simple program for printing the immediate relationships for some ego can be created:
uncle(X,Y) :- parent(Q,Y) , brother(X,Q).
aunt(X,Y) :- parent(Q,Y) , sister(X,Q).
nephew(X,Y) :- uncle(Y,X) , male(X).
nephew(X,Y) :- aunt(Y,X) , male(X).
niece(X,Y) :- uncle(Y,X) , female(X).
niece(X,Y) :- aunt(Y,X) , female(X).
son(X,Y) :- child(X,Y) , male(X).
daughter(X,Y) :- child(X,Y) , female(X).
grandmother(X,Y) :- grandparent(X,Y) , female(X).
relate(X,Y) :- mother(X,Y) , write('mother').
relate(X,Y) :- father(X,Y) , write('father').
relate(X,Y) :- brother(X,Y) , write('brother').
relate(X,Y) :- sister(X,Y) , write('sister').
relate(X,Y) :- son(X,Y) , write('son').
relate(X,Y) :- daughter(X,Y) , write('daughter').
relate(X,Y) :- uncle(X,Y) , write('uncle').
relate(X,Y) :- aunt(X,Y) , write('aunt').
relate(X,Y) :- grandmother(X,Y) , write('grandmother').
relate(X,Y) :- grandfather(X,Y) , write('grandfather').
relate(X,Y) :- niece(X,Y) , write('niece').
relate(X,Y) :- nephew(X,Y) , write('nephew').
relate(X,Y) :- first-cousin(X,Y) , write('first-cousin').
relate(X,Y) :- second-cousin(X,Y) , write('second-cousin').
(The predicate `write' simply puts the term in the parenthesis on the display
terminal. Since there are many relate rules, we need to know which one
applied. We could continue to extend relate indefinitely depending on the
relationships we wanted to be able to determine.)
The rule \fBrelate ill behave in different ways, depending on how it is
used. If it is invoked as:
?- \fBrelate(tom,john).
\fIfather yes
If invoked as:
?- \fBrelate(X,john).
Prolog would respond with the name of each person that had one of the above lationships to john, and the relationship, ie father, X = tom, brother, X = mike. If invoked as:
\fBrelate(john,X).
Prolog would respond with the name of each person who john had a relationship to, and the name of the relationship. And finally if invoked as:
\fBrelate(X,Y).
Prolog would print the name of each person, the name of the person they held a relationship to, and the relationship. Although the previous treatment is quite primitive (from a Prolog programmers point of view), it is quite suitable if the primary interest is in the actual relationships between individuals, or the number of relationships. As will be shown briefly in the next section it is possible to program such problems more elegantly (from various points of view), but if there is no direct interest in the means of determining the relationship, the method presented above is adequate, and usually much faster in development. For instance the above method took about 5 minutes to type in and 5 minutes to check for typing mistakes and test against data. The method presented in appendix II took about four hours to develop, and although more general, requires a substantially better knowledge of Prolog. One of the advantages of prolog to anthropologists with limited acquaintance with computers is that it can do very useful work with a minimum amount of programming skill.
ncestor(X,Y) :- father(X,Y).
ancestor(X,Y) :- father(X,Q) , father(Q,Y).
ancestor(X,Y) :- father(X,Q) , father(Q,W) , father(W,Y).
ancestor(X,Y) :- father(X,Q) , father(Q,W) , father(W,M) , father(M,Y).
in-lineage(X,Y) :- ancestor(X,Y).
in-lineage(X,Y) :- ancestor(Y,X).
in-lineage(X,Y) :- ancestor(Q,X) , ancestor(Q,Y) , X \= Y.
These rules assert that X and Y are in the same lineage if
ncestor(X,Y) :- father(X,Y).
ancestor(X,Y) :- father(Q,Y) , ancestor(X,Q).
The first rule for ancestor simply states that if X is father of Y, X is an ancestor of Y. If Prolog finds this to be the case, a solution is found, and Prolog will stop trying to find a solution. If this rule fails, then the second rule will apply, which attempts to find the father of Y, and if there is one applies the rule ancestor to the newly found father and X. Prolog will continue, first seeing if X is the father of the father of Y, and if not will proceed to the second rule, taking the father of the father of Y, and so on. If X is an ancestor to Y, eventually the first ancestor rule will apply, and Prolog will have found a solution. The first ancestor rule is essential, as it marks the boundary condition so that Prolog will not continue forever. This leads then to a general solution to the lineage problem, using the same definitions of \fBin-lineage
n-lineage(X,Y) :- ancestor(X,Y).
in-lineage(X,Y) :- ancestor(Y,X).
in-lineage(X,Y) :- ancestor(Q,Y) , ancestor(Q,X). For a more complex example of recursion refer to appendix II, which includes a
short Prolog program to find all the relationships between any two people in
an endogamous population.
Conclusion
Although the previous examples were rather naive formulations of simple
anthropological problems, these problems are not simple to represent in more
familiar programming languages.
This is a representation of knowledge, because from one primitive relationship,
a complex structure can be constructed and interrogated on the basis of rules.
What could be constructed by collecting data, all the relationships of
each member, is constructed by a single relationship, and the application of
rules defined in terms of that relationship, and other rule-derived
relationships. This construction has descriptive adequacy relative to its
ability to reflect the observed situation, and explanatory adequacy relative to
the researcher's ability to support and defend the rules that are evaluated.
As can be seen in the examples, Prolog is non-deterministic; two objects can
have several alternative relationships, and different expressions of rules can
co-exist.
With the help of informants, a system of native classes can be defined, and
Prolog can be used to test the consistency of the definitions formed by the
researcher, at least within the set of models Prolog is able to represent.
Prolog is best suited to a particular range of representations and models. It
is best where the problem domain can be represented as a structure, formed by
objects and relations between objects. Prolog's advantage in this domain
is the ability to represent the structure, without having to generate it
entirely in advance, and its capacity for handling multiple structures
superimposed over each other.
Prolog's limitations derive from a weakness in representing certain kinds of
processes, and a weakness in representing global context.
The first limitation is more a matter of ease of representation than
possibility. Simulations and other representations of process are often easier
to represent in procedural programming languages such as C or BASIC.
Still, with a good knowledge of Prolog it is possible to represent process.
Prolog works best and easiest
when most of the context is local to each rule, that is, Prolog's notion of variables
are essentially local.
Prolog has powerful facilities for building global structures, but they
are not as straight-forward or efficient to use as in some other languages.
Prolog's orientation is based on
rules `firing' or activating other rules... the
sequence of rules that apply in a case must be laid out in advance. Hence its
compatibility with structural descriptions. Production rules, such as those
used in expert systems [(.Fischer Expert.)], are based on data `firing' rules...
as the state of the global information changes, rules are applied to the data.
This latter approach is better suited to situations where the structure is not
clear.11
The case of Prolog's weaknesses can be overstated, they are simply less
convenient than those things Prolog does well.
Prolog offers substantial power for the representation of research materials in
a flexible way, with a minimum of assumptions built into the language that the
casual user cannot control. It matters not to Prolog whether the child
definition is a biological one or a social one. It is the researcher's
responsibility to select the facts to use, account for the meanings,
and to justify each of the rules used in the knowledge base.
Prolog simply provides facilities to relate data, information,
and knowledge together [(.barr all, fischer expert 1986.)].
Appendix I: Prolog Session Example
In the appendices \fBbold epresents what the user types into the computer, \fIitalics he responses by Prolog, and \s-6(small text)\s+6 represents comments on the dialogue, which do not appear in an actual session. The following (excepting the comments) is a record of a Prolog session. The examples given in the text are an edited version of this record.
\fIC-Prolog version 1.5
\fI[ Restoring file startup ]
\fI| ?- [user].
| male(john).
| **EOF**
(The first two lines are System messages printed on initialization.)
([user] is a way to get Prolog to take new rules from the keyboard, referred to
as `consult' mode in the text.)
(***EOF*** is a key used to indicate end of data. For UNIX (tm) EOF is [CTRL] D. In this context it will return the user to `query' mode.)
user consulted 68 bytes 0.0500003 sec.
yes
(`yes' indicates that Prolog was able to do what was asked; e.g. `success')
| ?- male(john).
yes
| ?- male(X).
X = john
yes
| ?- [user].
| male(tom).
| male(mike).
| **EOF**
user consulted 100 bytes 0.0333334 sec.
yes
| ?- male(X).
X = john ;
X = tom ;
X = mike ;
no
(`no' indicates failure. In this kind of query, it means that there are no
more cases that can satisfy male(X).)
| ?- [user].
| child(mike,tom).
| child(john,tom).
| **EOF**
user consulted 144 bytes 0.0166671 sec.
yes
| ?- child(mike,tom).
yes
| ?- child(tom,mike).
no
| ?- child(Kid,tom).
Kid = mike ;
Kid = john ;
no
| ?- child(Kid,Pater).
Kid = mike
Pater = tom ;
Kid = john
Pater = tom ;
no
| ?- [user].
| parent(X,Y) :- child(Y,X).
| father(X,Y) :- parent(X,Y) , male(X).
| **EOF**
user consulted 196 bytes 0.0500003 sec.
yes
| ?- father(tom,john).
yes
| ?- father(tom,Kid).
Kid = mike ;
Kid = john ;
no
| ?- [user].
| child(tom,carl).
| grandfather(X,Y) :- father(X,Q) , father(Q,Y).
| **EOF**
user consulted 148 bytes 0.0333342 sec.
yes
| ?- grandfather(Gf,john).
no
| ?- [user].
| male(carl).
| **EOF**
user consulted 32 bytes 2.77556e-17 sec.
yes
| ?- grandfather(Gf,john).
Gf = carl ;
no
| ?- [kinex].
(A prepared file can be included also, in this
case kinex, which is listed at the end of this appendix.
This file contains definitions for rules and facts needed
for the following dialogue.)
kinex reconsulted 4072 bytes 1.7 sec.
yes
| ?- relate(mike,X).
father
X = demeter ;
father
X = demeter ;
father
X = stephen ;
father
X = stephen ;
brother
X = john ;
brother
X = john ;
brother
X = paul ;
brother
X = paul ;
brother
X = jack ;
brother
X = jack ;
brother
X = nancy ;
brother
X = nancy ;
brother
X = jimmi ;
brother
X = jimmi ;
brother
X = nancy ;
brother
X = nancy ;
brother
X = jimmi ;
brother
X = jimmi ;
son
X = tom ;
son
X = tom ;
son
X = george ;
son
X = george ;
son
X = jean ;
son
X = jean ;
uncle
X = albert ;
uncle
X = simon ;
nephew
X = jim ;
nephew
X = jim ;
nephew
X = jim ;
nephew
X = jim ;
cousin
X = vikki ;
cousin
X = vikki ;
cousin
X = albert ;
cousin
X = albert ;
no
| ?- in-lineage(carl,mike).
yes
| ?- in-lineage(carl,X).
X = tom ;
X = tom ;
X = mike ;
X = mike ;
X = john ;
X = john ;
X = paul ;
X = paul ;
X = jack ;
X = jack ;
X = mike ;
X = mike ;
X = john ;
X = john ;
X = paul ;
X = paul ;
X = jack ;
X = jack ;
X = demeter ;
X = demeter ;
X = stephen ;
X = stephen ;
X = demeter ;
X = demeter ;
X = stephen ;
X = stephen ;
X = albert ;
X = albert ;
X = simon ;
X = simon ;
X = demeter ;
X = demeter ;
X = stephen ;
X = stephen ;
X = demeter ;
X = demeter ;
X = stephen ;
X = stephen ;
X = albert ;
X = albert ;
X = simon ;
X = simon ;
X = carl ;
X = carl
yes
| ?- **EOF**
[ Prolog execution halted ]
Contents of file kinex
male(john).
male(tom).
male(mike).
child(mike,tom).
child(john,tom).
parent(X, Y) :- child(Y, X).
father(X, Y) :- parent(X, Y) , male(X).
child(tom,carl).
grandfather(X,Y) :- father(X,Q) , father(Q,Y).
male(carl).
sibling(X,Y) :- parent(Q,X) ,
parent(Q,Y), not(X = Y).
brother(X,Y) :- sibling(X,Y) , male(X).
grand-parent(X,Y) :- parent(X,Q) , parent(Q,Y).
grandmother(X,Y) :- grandparent(X,Y) , female(X).
cousin(X,Y) :- parent(Q,Y) ,
sibling(Z,Q) , child(X,Z).
cousin(X,Y) :- grand-parent(Q,Y) ,
sibling(Z,Q) , child(X,Z).
cousin(X,Y) :- grand-parent(Q,Y) ,
sibling(Z,Q) , grand-parent(Z,X).
cousin(X,Y) :- parent(Q,Y) ,
sibling(Z,Q) , grand-parent(Z,Y).
uncle(X,Y) :- parent(Q,Y) , brother(X,Q).
aunt(X,Y) :- parent(Q,Y) , sister(X,Q).
nephew(X,Y) :- uncle(Y,X) , male(X).
nephew(X,Y) :- aunt(Y,X) , male(X).
niece(X,Y) :- uncle(Y,X) , female(X).
niece(X,Y) :- aunt(Y,X) , female(X).
son(X,Y) :- child(X,Y) , male(X).
daughter(X,Y) :- child(X,Y) , female(X).
relate(X,Y) :- mother(X,Y) , write('mother').
relate(X,Y) :- father(X,Y) , write('father').
relate(X,Y) :- brother(X,Y) , write('brother').
relate(X,Y) :- sister(X,Y) , write('sister').
relate(X,Y) :- son(X,Y) , write('son').
relate(X,Y) :- daughter(X,Y) ,
write('daughter').
relate(X,Y) :- uncle(X,Y) , write('uncle').
relate(X,Y) :- aunt(X,Y) , write('aunt').
relate(X,Y) :- grandmother(X,Y) ,
write('grandmother').
relate(X,Y) :- grandfather(X,Y) ,
write('grandfather').
relate(X,Y) :- niece(X,Y) , write('niece').
relate(X,Y) :- nephew(X,Y) , write('nephew').
relate(X,Y) :- cousin(X,Y) , write('cousin').
ancestor(X,Y) :- father(X,Y).
ancestor(X,Y) :- father(X,Q) , father(Q,Y).
ancestor(X,Y) :- father(X,Q) , father(Q,W) ,
father(W,Y).
ancestor(X,Y) :- father(X,Q) , father(Q,W) ,
father(W,M) , father(M,Y).
in-lineage(X,Y) :- ancestor(X,Y).
in-lineage(X,Y) :- ancestor(Y,X).
in-lineage(X,Y) :- ancestor(Q,X) ,
ancestor(Q,Y) , not(X = Y).
ancestor(X,Y) :- father(X,Y).
ancestor(X,Y) :- father(Q,Y) , ancestor(X,Q).
male(john).
male(tom).
male(jack).
male(mike).
male(paul).
male(george).
male(earl).
male(jim).
male(albert).
male(stephen).
male(simon).
male(carl).
female(janet).
female(mary).
female(carla).
female(sally).
female(jimmi).
female(nancy).
female(jean).
female(noddy).
female(vikki).
female(demeter).
female(wenonah).
child(mike,george).
child(nancy,george).
child(jimmi,george).
child(mike,jean).
child(nancy,jean).
child(jimmi,jean).
child(george,earl).
child(jim,earl).
child(george,noddy).
child(jim,noddy).
child(vikki,jim).
child(vikki,elizabeth).
child(albert,vikki).
child(albert,paul).
child(demeter,wenonah).
child(demeter,mike).
child(stephen,mike).
child(paul,mary).
child(paul,tom).
child(john,mary).
child(jack,tom).
child(simon,jack).
child(simon,carla).
child(carl,demeter).
child(carl,albert).
Appendix II: More Advanced Relate
The basic purpose of this example is to print on the display screen all relationships that can be calculated from the rules and facts given, in terms of a set of primitive or primary kin links; M F B Z S D H W. This is presented as an example of a slightly more complex Prolog program. It will be difficult to fully understand the program in kinrules (listed at the end) without one of the introductions to Prolog given in the bibliography. Also note that it is not as declarative as the prior example...it was found necessary to translate a procedure for determining relationships between people into semi-descriptive rules.
1} Prolog startup
C-Prolog version 1.5
[ Restoring file startup ]
| ?- [kinfacts].
kinfacts consulted 1748 bytes 0.6 sec.
yes
| ?- [kinrules].
kinrules consulted 4456 bytes 1.05 sec.
yes
(The above read two prepared sets of information into prolog, the contents of
which are included at the end of this text. The first is the asserted
relationships between individuals, and the second contains rules for deriving
relationships.
As demonstrated in Appendix 1, a prolog statement can be used to find different
information, depending on which terms are specified and which are left to
prolog to find. The following examples use a statement relate(Relative,Ego,Links)
which is designed to find all links between individuals in a given group. The
first example specifies Relative and Ego, and leaves prolog to find Links;
the relationship and intervening individuals in the relationship.)
| ?- relate(jimmi,mike,Links).
Z
Links = [jimmi] ;
Z
Links = [jimmi] ;
F B D S W F Z
Links = [george,jim,vikki,albert,demeter,mike,jimmi] ;
F B D S W F Z
Links = [george,jim,vikki,albert,demeter,mike,jimmi] ;
F B D S W F Z
Links = [george,jim,vikki,albert,demeter,mike,jimmi] ;
F B D S W F Z
Links = [george,jim,vikki,albert,demeter,mike,jimmi] ;
D H M F B D
Links = [demeter,albert,vikki,jim,george,jimmi] ;
D H M F B D
Links = [demeter,albert,vikki,jim,george,jimmi] ;
no
(There are no more Links.)
| ?- relate (carla,carl,Links).
M F F B D H B W
Links = [demeter,mike,george,jim,vikki,paul,jack,carla] ;
M F F B D H B W
Links = [demeter,mike,george,jim,vikki,paul,jack,carla] ;
F F B W
Links = [albert,paul,jack,carla] ;
no
| ?- relate(vikki,mike,Links).
F B D
Links = [george,jim,vikki] ;
F B D
Links = [george,jim,vikki] ;
D H M
Links = [demeter,albert,vikki] ;
no
| ?- relate(earl,nancy,Links).
F F
Links = [george,earl] ;
B D H M F F
Links = [mike,demeter,albert,vikki,jim,earl] ;
B D H M F F
Links = [mike,demeter,albert,vikki,jim,earl] ;
no
(The next example specifies Ego, but leaves prolog to find all Relatives and
the Links for those Relatives.)
| ?- relate(Who,demeter,Links).
S
Who = carl
Links = [carl] ;
B
Who = stephen
Links = [stephen] ;
F Z
Who = nancy
Links = [mike,nancy] ;
F Z
Who = jimmi
Links = [mike,jimmi] ;
F Z
Who = nancy
Links = [mike,nancy] ;
F Z
Who = jimmi
Links = [mike,jimmi] ;
F F B
Who = jim
Links = [mike,george,jim] ;
H M F B
Who = george
Links = [albert,vikki,jim,george] ;
F F B
Who = jim
Links = [mike,george,jim]
yes
(Here yes means that there are more, but I choose not to see them by not typing
a semi-colon at the end of the line.)
| ?- **EOF*
[ Prolog execution halted ]
Contents of file kinfacts
male(john).
male(tom).
male(jack).
male(mike).
male(paul).
male(george).
male(earl).
male(jim).
male(albert).
male(stephen).
male(simon).
male(carl).
female(janet).
female(mary).
female(carla).
female(sally).
female(jimmi).
female(nancy).
female(jean).
female(noddy).
female(vikki).
female(demeter).
female(wenonah).
child(mike,george).
child(nancy,george).
child(jimmi,george).
child(mike,jean).
child(nancy,jean).
child(jimmi,jean).
child(george,earl).
child(jim,earl).
child(george,noddy).
child(jim,noddy).
child(vikki,jim).
child(vikki,elizabeth).
child(albert,vikki).
child(albert,paul).
child(demeter,wenonah).
child(demeter,mike).
child(stephen,mike).
child(paul,mary).
child(paul,tom).
child(john,mary).
child(jack,tom).
child(simon,jack).
child(simon,carla).
child(carl,demeter).
child(carl,albert).
Contents of file kinrules
parent(X,Y) :- child(Y,X).
mate(X,Y) :- parent(X,Z) , parent(Y,Z) , X \= Y.
(This definition for mate is a bit weak, since only people who share a child in
the database can be considered mates by Prolog.
Also mate is extended to be used as
husband and wife in later rules. This could be made more accurate by including
a spouse relationship in the fact base.)
father(X,Y) :- parent(X,Y) , male(X).
mother(X,Y) :- parent(X,Y) , female(X).
sibling(X,Y) :- parent(Z,X) , parent(Z,Y) , X \= Y.
brother(X,Y) :- sibling(X,Y) , male(X).
sister(X,Y) :- sibling(X,Y) , female(X).
son(X,Y) :- child(X,Y) , male(X).
daughter(X,Y) :- child(X,Y) , female(X).
(The primary kin definitions.)
par(X) :- male(X), write('F ').
par(X) :- female(X), write('M ').
sib(X) :- male(X) , write('B ').
sib(X) :- female(X) , write('Z ').
kid(X) :- female(X) , write('D ').
kid(X) :- male(X) , write('S ').
spo(X) :- male(X) , write('H ').
spo(X) :- female(X) , write('W ').
(These definitions simply write on the terminal a letter corresponding to a
primary kin link. They should properly place the term in a list, but that
would make the program even less clear.)
member(X,[X|_]).
member(X,[_|Y]) :- member(X,Y).
ident(X,Y) :- not(member(X,Y)).
(member determines if X is in the list Y. ident uses member,
and succeeds if X is not in the list. This
is necessary to avoid looping in the kin network ie finding relationships that
contain a linking person more than once.)
relateA(X,Y,[Z|R],R) :- child(X,Y) , kid(X).
relateA(X,Y,[Z|R],R) :- sibling(X,Y) , sib(X).
relateA(X,Y,[P|Z],R) :- sibling(Q,X),ident(Q,Z),
relateB(Q,Y,[P|[Q|Z]],R),sib(X).
relateA(X,Y,[P|Z],R) :- parent(Q,X),ident(Q,Z),
relateA(Q,Y,[P|[Q|Z]],R), kid(X).
(relateA is true if X is child or sibling of Y, and writes the appropriate
primary kin type on the terminal. If neither is true, then the we find a
sibling Q, of X, check to see if this sibling Q is in the list of linking
relatives so far, and if not in the list use the relateB rule to relate Q to Y
instead, and if a relation is found write the appropriate sib link on the
terminal. Otherwise we find a parent Q, of X, look Q up on the list, and then
apply relateA to Q and Y instead of X and Y. This is a recursive rule, which
can be thought of as using the rule to solve a little of the problem at a
time. The bracketed notation ie [P|Z] is a Prolog method of representing
lists (where P is the first thing in a list, and Z is the remainder of the
list), and is used here to maintain the list of linking relatives. The
definitions for relateB, following are similar. relateA and relateB represent
a kin-system dependant method of tracing relationships between people, and
would require some alteration for other systems if we wanted terminological
definitions rather than fictive biological relations. If we were to keep the
fictive biological links in a list, rather than print them on a terminal, then
it would be possible to relate them to terminological rules.)
relateB(X,Y,[Z|R],R) :- parent(X,Y) , par(X).
relateB(X,Y,[Z|R],R) :- mate(X,Y) , spo(X).
relateB(X,Y,[P|Z],R) :- mate(Q,X),ident(Q,Z),
relateA(Q,Y,[P|[Q|Z]],R), spo(X).
relateB(X,Y,[P|Z],R) :- child(Q,X),ident(Q,Z),
relateB(Q,Y,[P|[Q|Z]],R), par(X).
relate(X,Y,R) :- relateA(X,Y,[Y,X],R).
relate(X,Y,R) :- relateB(X,Y,[Y,X],R).