If you are just interested in a correct solution, take @PauloMoura's solution. Here is what I think the intention of this exercise was. Take your original program, it seems (at first sight), that one can remove the goal even(N)
in the second clause.
But before that, let me make clear that the predicate name doubles/2
is a misnomer. I'd rather say list_semidoubled/2
...
odd(N) :-
N mod 2 =:= 1.
double([], []).
double([N|L], [N|D]) :-
odd(N), !, double(L, D).
double([N|L], [M|D]) :-
% even(N),
M is 2*N, double(L, D).
However, the cut above does a little bit more than we expected. It does not cut when odd(N)
is true alone, but there is a tiny little extra condition that sneaked into our program. Do you see it? Let's have a look at the relevant part:
double([N|L], [N|D]) :-
odd(N), !, ...
There is odd(N)
, but look above! In the head another condition is lying there. And it waits until it can wreak havoc! The "hidden" condition is:
If N
is equal (unifies) to the first element in the second argument!
Let's try it out!
?- double([1], Xs).
Xs = [1].
Worx as expected, does it not. However:
?- double([1], [2]).
true.
Tiens, what is happening here? That should fail!
A predicate that behaves in that way lacks steadfastness. We would expect the query to fail, for Prolog did not show us this solution.
So the cut above does not always cut as you expected, but a little less than that. Errors as these are often quite difficult to locate, so it is a good idea to avoid them right from the beginning. You have several options:
1 Stick to pure, monotonic Prolog
This is probably the best for beginners. And it is not necessarily less efficient. I'd rather:
double(Xs, Ys) :
maplist(n_semidoubled, Xs, Ys).
n_semidoubled(X, Y) :-
M is X mod 2,
( M = 0,
Y is X*2
; M = 1,
Y is X
).
This can be improved to — slightly hackerish:
n_semidoubled(X, Y) :-
Y is X*(2-X mod 2).
2 Use (\+)/1
, once/1
, if-then-else
@PaulMoura already showed you one such possibility. Another is to use (\+)/1
:
n_semidoubled(X, X) :-
odd(X).
n_semidoubled(X, Y) :-
\+odd(X),
Y is X*2.
3 Reduce the scope of the cut
If you are now still determined to use this construct, try to restructure your program such that the scope of the cut is as local as possible. That is, instead of placing the cut in the recursive rule, rather make a local predicate:
doubles([], []).
doubles([E|Es], [M|Ms]) :-
n_semidoubled(E, M),
doubles(Es, Ms).
n_semidoubled(E, M) :-
odd(E),
!,
M is E.
n_semidoubled(E, M) :-
M is E*2.
There is another anomaly in your original program which is independent of the cut issue. Consider:
?- double([1*1],Xs).
Xs = [1*1].
double([N|L], [X|D]) :- ( even(N) -> X is 2*N ; X = N ), double(L, D).
– lurker