10
\$\begingroup\$

As an assignment for a class, I have made a small Harry Potter family tree in Prolog. It compiles into a self-contained executable that can be run straight from the command line on Linux machines. This is my second ever program in Prolog, so I am still learning. My background is in imperative languages, so I am probably doing things and formatting my code in a not very "Prolog-ish" manner.

Main

:- [pft].

print_query_true(Q) :-
    forall(Q, writeln(true:Q)).

print_query_false(Q) :-
    forall(\+ Q, (writeln(false:Q), nl)).

get_input :- 
    writeln('Enter "1." to run a query or "9." to exit:'), 
    read(I), 
    (
        I = 1 -> run_query;
        I = 9 -> writeln('Goodbye'), halt;
        writeln('Invalid input!')
    ).

run_query :- 
    writeln('Enter your query, followed by a period (.):'), 
    read(I),
    nl,
    print_query_true(I),
    print_query_false(I),
    nl.

main :- repeat, get_input, fail.

PFT

%male(X)
male(fleamont).
male(james).
male(harry).
male(albus).
male(james_ii).
male(arthur).
male(bill).
male(charlie).
male(percy).
male(george).
male(fred).
male(ron).
male(hugo).
male(mr_granger).

%female(X)
female(euphemia).
female(lily).
female(ginny).
female(lily_ii).
female(molly).
female(mrs_granger).
female(hermione).
female(rose).

%parent(Parent, Child)
parent(fleamont, james).
parent(euphemia, james).
parent(james, harry).
parent(lily, harry).
parent(harry, albus).
parent(harry, james_ii).
parent(harry, lily_ii).
parent(ginny, albus).
parent(ginny, james_ii).
parent(ginny, lily_ii).
parent(arthur, bill).
parent(arthur, charlie).
parent(arthur, percy).
parent(arthur, fred).
parent(arthur, george).
parent(arthur, ron).
parent(arthur, ginny).
parent(molly, bill).
parent(molly, charlie).
parent(molly, percy).
parent(molly, fred).
parent(molly, george).
parent(molly, ron).
parent(molly, ginny).
parent(ron, rose).
parent(ron, hugo).
parent(hermione, rose).
parent(hermione, hugo).
parent(mr_granger, hermione).
parent(mrs_granger, hermione).

%married(Husband, Wife)
married(fleamont, euphemia).
married(james, lily).
married(harry, ginny).
married(arthur, molly).
married(ron, hermione).
married(mr_granger, mrs_granger).

%spouse(X, Y)
spouse(X,Y) :- married(X, Y); married(Y, X).

%father(Father, Child)
father(F, C) :- parent(F, C), male(F).

%mother(Mother, Child)
mother(M, C) :- parent(M, C), female(M).

%brother(Brother, Person In Question)
brother(B, PIQ) :- male(B), sibling(B, PIQ).

%sister(Sister, Person In Question)
sister(S, PIQ) :- female(S), sibling(S, PIQ).

%child(Child, Parent)
child(C, P) :- parent(P, C).

%son(Son, Parent)
son(S, P) :- child(S, P), male(S).

%daughter(Daughter, Parent)
daughter(D, P) :- child(D, P), female(D).

%grandchild(Grandchild, Grandparent)
grandchild(GC, GP) :- child(GC, P), child(P, GP).

%grandson(Grandson, Grandparent)
grandson(GS, GP) :- grandchild(GS, GP), male(GS).

%granddaughter(Granddaughter, Grandparent)
granddaughter(GD, GP) :- grandchild(GD, GP), female(GD).

%sibling(X, Y)
sibling(X, Y) :- parent(P, X), parent(P, Y), X \= Y.

%grandparent(Grandparent, Grandchild)
grandparent(GP, GC) :- grandchild(GC, GP).

%grandfather(Grandfather, Grandchild)
grandfather(GF, GC) :- male(GF), grandparent(GF, GC).

%grandmother(Grandmother, Grandchild)
grandmother(GM, GC) :- female(GM), grandparent(GM, GC).

%brother_in_law(Spouses Brother, Person In Question)
brother-in-law(SB, PIQ) :- brother(SB, Spouse), spouse(PIQ, Spouse).

%sister_in_law(Spouses Sister, Person In Question)
sister-in-law(SS, PIQ) :- sister(SS, Spouse), spouse(PIQ, Spouse).

%aunt(Aunt, Person In Question)
aunt(A, PIQ) :- sister(A, P), parent(P, PIQ).

%uncle(Uncle, Person In Question)
uncle(U, PIQ) :- brother(U, P), parent(P, PIQ).

%niece(Niece, Person In Question)
niece(N, PIQ) :- daughter(N, P), sibling(P, PIQ).

%nephew(Nephew, Person In Question)
nephew(N, PIQ) :- son(N, P), sibling(P, PIQ).

%descendant(Descendant, Ancestor)
descendant(D, A) :- parent(A, D).
descendant(D, A) :- parent(P, D), descendant(P, A).

%ancestor(Ancestor, Descendant)
ancestor(A, D) :- descendant(D, A).

%closest_common_ancestor(Person1, Person2, Closest Common Ancestor)
closest_common_ancestor(P1, P2, CCA) :- 
    ancestor(CCA, P1), 
    ancestor(CCA, P2),
    forall(child(C, CCA), (not(ancestor(C, P1)), not(ancestor(C, P2)))),
    P1 \= P2.

%write_descendant_chain(Descendant, Ancestor)
write_descendant_chain(D, A) :- 
    ( 
        child(D, A) -> write_child(D, A); 
        child(D, P), 
            descendant(P, A), 
            write_child(D, P), 
            write_descendant_chain(P, A)
    ).

%write_child(Child, Parent).
write_child(C, P) :- 
    child(C, P), 
    write(C), 
    write(' is a child of '), 
    write(P), 
    nl.
\$\endgroup\$
  • 3
    \$\begingroup\$ How can I modify the program to represent Star wars family? I am your father - Noooooooooooooooooo. \$\endgroup\$ – Bruno Costa Sep 26 '16 at 21:46
  • 1
    \$\begingroup\$ Its nice to see they are still teaching prolog. I loved that language. But unfortunately its so long since I used it that my advice will be way out of date. \$\endgroup\$ – Martin York Sep 26 '16 at 22:05
3
\$\begingroup\$

Your closest_common_ancestor/3 doesn't work, for instance, closest_common_ancestor(rose, fred, X). fails to find answers, however X=arthur and X=molly are both answers.

Below is the version that would work:

closest_common_ancestor(P1, P2, CCA) :-
    parent(CCA, P1),
    parent(CCA, P2).
closest_common_ancestor(P1, P2, CCA) :-
    parent(P, P1),
    closest_common_ancestor(P, P2, CCA).
closest_common_ancestor(P1, P2, CCA) :-
    parent(P, P2),
    closest_common_ancestor(P1, P, CCA).

Some general advise:

  1. There's no reason not to name variables in predicates the way you described them in the documentation. In fact, I'd prefer Person1 to P1, especially in the context where Parent1 could be also inferred. Also, there are some documentation tools, usually bundled with whatever version of Prolog you are using, that have certain conventions wrt documentation format. If you wanted to produce documentation that is usable for others in an automatic way, it would help to study those tools. For example, if you are using swipl, look at http://www.swi-prolog.org/pldoc/doc_for?object=section(%27packages/pldoc.html%27) .
  2. Try avoiding expressing your assertions through negation. It is an art in and for itself, but once you start following this rule, you'll realize that the code you produce is more straight-forward and less verbose. It takes, however, time and effort to adopt this rule.
  3. Use format instead of print, nl and friends. Unless you are writing some utility code whose purpose is printing, there's no reason to clutter the code with string formatting.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.