Re: where do I think wrong

"Richard A. O'Keefe" <[email protected]>
Newsgroups gmane.comp.ai.prolog.swi
Message-ID <[email protected]>
On 13/03/2014, at 9:04 PM, Roelof Wobben wrote:
> 
> parent(mans,gerda).
> parent(mans,roelof).
> parent(marie,gerda).
> parent(marie,roelof).
> parent(dagmar,denise).
> parent(dagmar,kimberly).
> parent(ronald,denise).
> parent(ronald,kimberly).
> parent(chantal,tamara).
> parent(roelof,tamara).
> parent(jan,chantal).
> parent(jan,dagmar).

(1) Where is the comment saying which argument is what?
    Is mans a parent of gerda, or
    is gerda a parent of mans?

    I entreat you in the name of suffering humanity,
    ADD A COMMENT
    % parent(P, C) is true when P and C are atoms
    % naming people and P is one of C's parents.

    I am serious about the "suffering humanity" part.
    Failing to provide this information waste people's
    time as they scour the rest of your program for
    clues about something they needed to know and you
    knew and chose to conceal.  I don't care *what*
    the programming language is, that's bad style.

(2) There is a key on your keyboard you may have
    forgotten about, though it's easily the biggest
    and easiest to use.  USE IT.  It's called the
    space bar, and it's the big one at the bottom.

    Like most programming languages, Prolog uses the
    comma for several things:
	- to separate goals
	=> put a newline after
	- to separate the arguments of goals
	=> put a space after
	- to separate the fields of data structures
	=> I usually prefer NOT to put a space, but
	   there are some who do, and I'll not say
	   they're wrong.

    When a construction has parts that need to be
    *perceived* as parts, ensure that there is
    enough white space.

    Again, this is largely language-independent.
    If there is any programming language in which
    it is good style to run the arguments of a
    procedure together in one space-less blob, I
    hope I never see it.

> is_father(F,C) :- 
>   parent(F,C), male(F).

Better:

    father_child(Father, Child) :-
        parent_child(Father, Child),
        male(Father).

(3) You can get away with one-letter variable names in
    toy programs, but it is a cruel habit to get into.
    I once had to maintain a couple of thousand lines
    of Prolog where every variable was one letter or
    one letter and one digit, and I couldn't take a
    step until I had changed all the names to something
    meaningful.

    Again, this is largely language-independent.
    BASIC was the last programming language I know of
    to insist on 1-letter or 1-letter-1-digit names,
    and modern BASIC doesn't do that any more.

    What if someone wrote

	is_father(M,T) :- parent(M,T), male(M).

    (Looks obvious to _me_: M = Matua, T = Tamaiti.)
    
> have_children(M,F) :-
>   is_mother(M,Z), 
>   is_father(F,Z).

(3) To start with, this is a perfect example of how
    one-letter names can confuse.  I initially read
    "M" and "F" as "Male" and "Female" respectively...

(4) If you say in English that M and F "have children"
    you are definitely implying that they have more than
    one.  This predicate only tests whether they have at
    least one.

(5) What's more, suppose

	richard father-of abigail
	richard father-of susanna
	jeanene mother-of abigail
	jeanene mother-of susanna

    Then
	have_children(richard, jeanene)
    can be proven true in two different ways.
    You get this interaction:

	?- have_children(Woman, richard).
	Woman = jeanene ;
	Woman = jeanene. 

    It is important to understand that Prolog doesn't find
    *solutions* to your query, it finds *proofs*, and one
    solution may have many proofs.

    We can fix that by putting hacky code here,
    but the best way is to refactor the data design.
    It's certainly what I would do if this were a
    real data base.

    I would introduce 'consortships'.  (OED sense 2b.)

    So there would be facts

	consortship_man(  273, richard).
	consortship_woman(273, jeanene).

	parentage(abigail, 273).
	parentage(susanna, 273).

    or something like that, and rules

    man_and_woman_who_had_some_child(Man, Woman) :-
	consortship_man(Consortship, Man),
	consortship_woman(Consortship, Woman),
	once(parentage(_Child, Consortship)).

    child_father(Child, Father) :-
	parentage(Child, Consortship),
	consortship_man(Consortship, Father).

    or something like that.

> list_parents(C) :-
>   parent(X,C), 
>   tab(5), 
>   write(X),
>   nl, 
>   fail.
>   list_parents(_).

(6) I really dislike having the parts of a failure-driven loop
    scattered across several clauses.  I find it clearer if
    you have

    list_parents(Child) :-
	(   parent(Parent, Child),
	    tab(5), write(Parent), nl,
	    fail
	;   true
	).

> display_parents(C) :-
>   write('De ouders van '), 
>   write(C), 
>   write(' zijn :'),
>   nl, 
>   nl,
>   list_parents(C).
>   display_parents(_).

(7) Here the second clause is worse than useless.
> 
> have_siblings(C) :-
>    is_father(X,C),
>    is_father(X,S), 
>    is_mother(Y,S), 
>    is_mother(Y,C), 
>    C \= S,
>    write(S).

(8) You really want to factor out the common pattern here.

    child_father_mother(Child, Father, Mother) :-
        is_father(Father, Child),
	is_mother(Mother, Child).

    have_siblings(Child) :-
	child_father_mother(Child, Father, Mother),
	child_father_mother(Sibling, Father, Mother),
	Sibling \== Child,
	write(Sibling).

(9) You have a predicate whose purpose is to LIST
    the siblings of someone, but you have named it
    as if its purpose were to TEST whether someone
    had any siblings.  Write

    are_full_siblings(Child, Sibling) :-
	child_father_mother(Child, Father, Mother),
	child_father_mother(Sibling, Father, Mother),
	Sibling \== Child.

    list_full_siblings_of(Child) :-
	foreach(are_full_siblings(Child, Sibling),
	    writeln(Sibling)).

> display_siblings(C) :-
>   write('De broers/zusters van '), 
>   write(C), 
>   write(' zijn :'),
>   nl, 
>   nl,
>   have_siblings(C).
>   display_siblings(_).

(7) again.  Or it would be if have_siblings/1 were the
    other half of the failure-driven loop you think it is.
    With the list_siblings/1 predicate above, delete that
    second clause.
> 
> 
> It worked fine if a mother and father are known.
> So I want to change it to this :
> 
> have_siblings(C) :-
>    is_father(X,C),
>    is_father(X,S); 
>    is_mother(Y,S), 
>    is_mother(Y,C), 
>    C \= S,
>    write(S).

(10)You have a scope issue with ';'.

    ALWAYS write
	(   <alternative 1>
	;   <alternative 2>
	...
	;   <alternative n>
	)
    with the semicolons at the front, so they are
    visibly NOT commas, and while there are times
    that you don't really need the parentheses,
    ALWAYS put them in.

    Do NOT put extra parentheses around each alternative,
    they just make it harder to read.

    Again, you want to factor out "are these people half
    siblings" from "show the half siblings of this person".

    are_half_or_fullsiblings(Child, Sibling) :-
	(   is_father(Father, Child),
	    is_father(Father, Sibling)
	;   is_mother(Mother, Child),
	    is_mother(Mother, Sibling)
	),
	Sibling \== Child.

    list_half_siblings(Child) :-
	foreach(are_half_or_full_siblings(Child, Sibling),
	    writeln(Sibling)).

    However, note that abigail and susanna are full siblings,
    so they have the same father AND the same mother, so there
    are TWO ways for are_half_siblings(abigail, susanna) to be
    proven, and list_half_siblings(abigail) will write
    susanna twice.

    One way to deal with that is to say that two people
    are half_or_full siblings if they have the same father
    or they have the same mother but NOT the same father.

    % In the following predicates, X and Y stand for
    % people of indeterminate sex at the same genealogical level.

    same_father(X, Y) :-
        is_father(Father, X),
	is_father(Father, Y).

    same_mother(X, Y) :-
	is_mother(Mother, X),
	is_mother(Mother, Y).

    half_siblings(X, Y) :-
	(   same_father(X, Y),
	    \+ same_mother(X, Y)
	;   same_mother(X, Y),
	    \+ same_father(X, Y)
	).

    half_or_full_siblings(X, Y) :-
        (   same_father(X, Y),
	    X \== Y
	;   same_mother(X, Y),
	    \+ same_father(X, Y)
	).

    full_siblings(X, Y) :-
	same_father(X, Y),
	X \== Y,
	same_mother(X, Y).

    You can get away with using single-letter variables
    if you set up an EXPLICIT convention for what they mean.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.