Re: how to refractor this ?

"Richard A. O'Keefe" <[email protected]>
Newsgroups gmane.comp.ai.prolog.swi
Message-ID <[email protected]>
On 19/03/2014, at 5:07 AM, Roelof Wobben wrote:

> I have this piece: 
> 
> siblings(Child, Sibling) :-
>   same_father(Child, Sibling),
>   Child \= Sibling.
> siblings(Child, Sibling) :-
>   same_mother(Child, Sibling),
>   Child \= Sibling.
> 
> display_siblings(X,Y):-
>     setof(X-Y, (siblings(X,Y), X @< Y), Sibs),
>     write(Sibs).
> 
> brother_child(Child, Brother) :- 
>     siblings(Child, Brother), 
>     male(Brother).
> 
> display_brother(X,Y) :-
>      setof(X-Y, (brother_child(X,Y), X @< Y),Brother),
>      write(Brother).
> 
> sister_child(Child, Sister) :- 
>     siblings(Child, Sister), 
>     female(Sister).
> 
> display_sister(X,Y) :-
>      setof(X-Y, (sister_child(X,Y), X @< Y),Sister),
>      write(Sister).
> 
> As you can see the display are almost the same except the calling rule and the name of the variable.
> 
> Is there a way I can make one big display function. 
> and can I refractor more here ?

(1) The word is 'refactor' = 're-' + 'factor'.

(2) Before considering refactoring, you should ask whether
    the code in fact works.

    If you want to display the sisters of a Person,

	display_sisters(Person) :-
	    person_sister(Person, Sister),
	    writeln(Sister),
	    fail ; true.

    will do the job.  In particular, if a Person has no
    sisters, it will quietly write nothing.

    However, in that case, your

	display_system(Person, _)

    will FAIL.

    The reason for this is that setof(Template, Goal, Set)
    will backtrack over bindings for variables in Goal
    that do not occur in Template.  (If it did not do this
    nested setof calls would not work.)  When there is no
    binding for such variables, the query fails.  This is
    not a bug or an accident but an *essential* feature
    of the setof/3 interface if it is to work nested.

    When all of the variables in Goal *happen* to be
    included in Template, that is NOT treated as a special
    case.

    So
	setof(X-Y, (sister_child(X,Y), X @< Y), Sister),
	write(Sister)
    is very nearly equivalent to
	sister_child(X, Y), X @< Y, Sister = X-Y,
	write(Sister).

    Start by asking:
	is display_sister(X, Y) supposed to be called
	with X and Y both known?
	what is it supposed to do then?
	in particular, what if sister(X,Y) is false,
	or sister(X,Y) is true but X @>= Y?

	is display_system(X, Y) supposed to be called
	with X known and Y still a variable?
	what is it supposed to do then?
	why is it considered a good idea that Y should
	still be an unbound variable afterwards?
	what is supposed to happen if there is no
	value for Y making sister(X, Y) true?

	the same, but with Y known and X a variable.

	is display_system(X, Y) supposed to be called
	with X and Y still both variables?
	what is it supposed to do then?
	why is it considered a good idea that X and Y
	should still be unbound variables afterwards?
	what is supposed to happen if there are no sisters?

(3) We come back to your continued failure to write any
    COMMENTS.  How are we supposed to know your intentions
    when you don't TELL us what your intentions are?
    We can't infer them from the code because the code
    looks *wrong*, as in "I can't think of any plausible
    use for code that looks like that".  Certainly such
    a change in behaviour (from *succeed* when there are
    no such relatives to *fail* when there are no such
    relatives) should most definitely have been signalled
    by a comment if that was what you meant.

    The very first lesson in becoming a good programmer in
    any language is "before you write any procedure,
    WRITE A COMMENT stating your intentions".  It does not
    have to be a big comment, or follow the heavyweight
    markup conventions of some stupid tool like JavaDoc.
    It has to give a human being a clue or two about what
    is supposed to happen.  And the first person to be
    helped is *YOU*.

    If you are following rigorous naming conventions, as
    recommended by Bertrand Myer in OOSC2, for example,
    you can omit a comment whose content is predictable
    from the procedure's name.  You can even do this if
    you are following a project-local or even file-local
    convention as long as that convention is itself
    DOCUMENTED.

    Let me give a compelling example of the need for comments
    from your own code:

> siblings(Child, Sibling) :-
>   same_father(Child, Sibling),
>   Child \= Sibling.
> siblings(Child, Sibling) :-
>   same_mother(Child, Sibling),
>   Child \= Sibling.

    If Child and Sibling are full siblings (same father and
    same mother but not same father) then
    siblings(Child, Sibling)
    will succeed twice, since there are two different ways
    to prove it.  (SQL agrees with Prolog here.)

    MAYBE THAT'S WHAT YOU WANT, though it seems extremely
    unlikely.  We cannot tell whether I've just noticed a
    bug in your code or whether you just have strange
    intentions, because *you* knew what you wanted this
    to do but decided to hide your intentions from us.

    Now, since I posted a definition

	full_or_half_siblings(Child, Sibling) :-
	    same_father(Child, Sibling),
	    Child \== Sibling.
	full_or_half_siblings(Child, Sibling) :-
	    same_mother(Child, Sibling),
	    \+ same_father(Child, Sibling).

   this siblings/2 looks like a garbled copy of that
   full_or_half_siblings/2.  So I am pretty sure it
   is a "sloppy copying" bug.  But I can't be CERTAIN
   because YOU DID NOT SAY WHAT YOU MEANT TO HAPPEN.

   Note that using full_or_half_siblings/2, which takes
   simple care NOT to repeat solutions, means that there
   isn't any point in using setof/3 in the other predicates.

   There are a number of great slogans about programming.
   "If it isn't tested it doesn't work" is one.
   Here's another: "If it has no useful comments it's wrong."

(4) I thought the following code had already been posted
    in this thread.

	:- meta_predicate display_answers(1).

	display_answers(Property) :-
	    call(Predicate, Answer),
	    writeln(Answer),
	    fail ; true.

(5) By the way, I don't really like output where many
    different commands have output that can't be
    distinguished.  I would much rather see

	display_sisters(Person) :-
	    write('% sisters of '), write(Person), nl,
	    (   sister(Person, Sister),
	        portray_clause(sister(Person, Sister)),
	        fail
            ;   true
            ),
	    write('% end'), nl.

    portray_clause/1 is a way of writing a something so
    that it looks like a fact that you could paste into
    Prolog source code.
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.