Prolog list exercise

Viewed 59

Here is my exercise: define a predicate in prolog which, applied to a list L of integers, gives as a result the list of the successors of the even elements present in L.

The code i wrote work only if one element is even , can someone help me figure out where I'm wrong?

even(X):- 0 is mod(X,2).
odd(X):- 1 is mod(X,2).

list_even([],[]).    
list_even([T|C],K):- even(T), E is T+1, list_even(C,K1), append(K1,E,X), K is X.
list_even([T|C],K):- odd(T),list_even(C,K).

2 Answers

Some flaws in your code are:

  • append(K1, E, X): This goal should append two lists to generate a third list; however, E is just an element, not a list. So you need to write append(K1, [E], X).
  • K is X: This goal evaluates the arithmetic expression represented by the variable X and unifies its value with variable K; however, X is a list, not an arithmetic expression. So you need to remove it.

So the correct code would be as follows:

list_even([], []).
list_even([T|C], K):- even(T), E is T+1, list_even(C, K1), append(K1, [E], K).
list_even([T|C], K):- odd(T), list_even(C, K).

Example:

?- list_even([1, 20, 3, 40, 5, 7, 8], Successors).
Successors = [9, 41, 21] ;
false.

Note that in the obtained answer, the successors appear in reverse order (the first even element in the input list is 20, but its successor 21 is the last element in the output list).

So a better code is as follows:

even_number_successors([], []).
even_number_successors([X|Xs], Successors) :-
   (   X mod 2 =:= 0
   ->  Y is X + 1,
       Successors = [Y|Ys]
   ;   Successors = Ys ),
   even_number_successors(Xs, Ys).

Example:

?- even_number_successors([1, 20, 3, 40, 5, 7, 8], Successors).
Successors = [21, 41, 9].

From your problem statement,

define a predicate in prolog which, applied to a list L of integers, gives as a result the list of the successors of the even elements present in L.

  • On the face of things, you don't need odd/1: that which is not even is odd.

  • Why use append/3? You're just iterating/filtering a list and constructing a new one, right? Instead, you can construct the list in order by leaving the list incomplete, with an unbound tail. As we recurse down, we build out the tail of the list, either by unifying it with a new list, or at the end, unifying it with the empty list, the atom []).

I would do something like this:

list_even( []     , []     ) .  % empty list? All done.
list_even( [X|Xs] , [Y|Ys] ) :- % non-empty?
  even(X),                      % - is X even? if so...
  !,                            % - eliminate the choice point
  Y is X+1,                     % - get the successor of X
  list_even(Xs,Ys)              % - recurse down
  .                             %
list_even( [_|Xs] ,    Ys  ) :- % Otherwise, the head of the list is odd: discard the head, and...
  list_even(Xs,Ys)              % - recurse down
  .                             % Easy!

even(X) :- 0 =:= X .
Related