Perhaps the first principle is to use meaningful and accurate
names for variables and predicates. If your predicate is supposed to check if
a number is even, you can call it
is_even. If you later change
it so that it also calculates the contribution, for the case of even
numbers, to some total that you are calculating, then you need to change the
name of the predicate to reflect this. Maybe you can call it
even_contribution, and put a comment where the predicate starts,
stating exactly what the predicate does, in more detail than you can fit
into a predicate name.
Don't reinvent things already available in Prolog.
Here's an example of some code produced by a novice Prolog programmer:
mymember(First, [Second | _]) :- member(First, [Second]).First of all,
member(First, [Second])is identical to
First = Second. So why not say so:
mymember(First, [Second | _]) :- First = Second.But then, why have the explicit unification of
Second- so instead:
mymember(First, [First | _]).At this point, we can see that mymember is a misleading name for this predicate - which is actually checking whether its first argument is the same as the first member of the list that is the second argument. So the name of the predicate is misleading, as well as unclear.
member, in Prolog, means check the whole list for the presence of the first argument, whereas this predicate only ever looks at the first element of the list. The work that this predicate is doing has no obvious name, and that suggests that it is not a useful abstraction of the problem being solved. The problem that the novice programmer was trying to solve was to find out if a list had successive elements that were the same, as happens for
b, but not
a. in the list
[a, b, b, c, d, a]. So a better way would be like this:
has_repeats() :- fail. % unnecessary rule - see below has_repeats([_OnlyOneItem]) :- fail. % unnecssary rule - see below has_repeats([First, First | Rest]). has_repeats([First, Second | Rest]) :- First \= Second, has_repeats([Second | Rest]).In this code, the rule in red is the one that corresponds to what the novice programmer was trying to do with "
failcan be omitted - they are only there to make it clear what happens with empty lists and lists with one member.
Don't have unused "junk" code. Obviously, it is worse still
to have used junk code - that is, wrong code. However, unused
junk code is confusing for the reader and demonstrates that the programmer
who wrote it was confused. So how can you tell if code is unused junk?
The best way, I suppose, is to have a thorough understanding of the code
you are writing, and then you'll see that the code is junk. Failing that,
when you test the code, you can put in calls to the built-in Prolog
thing(X, Y) :- print('Entering rule 3 for predicate *thing*'), ... % rest of goals for this rule .Then you try to generate a test case which will make use of your rule in finding it's first solution:
?- thing(A, [cat, dog, mouse]). % replace with actual parameters % that are supposed to test rule 3 of the predicate called thing A = 3.If, as in the example dialogue, the