Re: where do I think wrong
Roelof Wobben <[email protected]>
| Newsgroups | gmane.comp.ai.prolog.swi |
|---|---|
| Message-ID | <[email protected]> |
> Subject: Re: [SWIPL] where do I think wrong > From: [email protected] > Date: Fri, 14 Mar 2014 10:20:28 +1300 > CC: [email protected] > To: [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. > > Thanks for the long explanation.One last question where does /+ for.I know /== is not the same. Roelof -------------- next part -------------- HTML attachment scrubbed and removed