General
Material
Lecture 1
Lecture 2
Lecture 3
Lecture 4
Lecture 5

Solution

%% If the input list is empty, the output list has 
%% to be empty as well.
replace_a_b_c([],[]).

%% If the input list is not empty, there are four cases.
%% First case: the first element is an a. The first element 
%% of the output list has to be a b. OutTail should be what
%% you get what properly replacing all a's, b's, and c's in InTail.
replace_a_b_c([a|InTail],[b|OutTail]) :-
              replace_a_b_c(InTail,OutTail).

%% Second case: the first element is a b.
replace_a_b_c([b|InTail],[c|OutTail]) :-
              replace_a_b_c(InTail,OutTail).

%% Third case: the first element is a c.
replace_a_b_c([c|InTail],[a|OutTail]) :-
              replace_a_b_c(InTail,OutTail).

%% Fourth case: the first element is something else.
replace_a_b_c([X|InTail],[X|OutTail]) :-
              replace_a_b_c(InTail,OutTail).
     
Download the knowledge base here.

This predicate takes a list and replaces all a's by b's, all b's by c's, and all c's by a's. However, if you force Prolog to backtrack (using the semicolon), it will give you other solutions as well. Try it out. Do you see why this is the case and how it can be fixed?

The problem is due to the definition of the last rule. It does not explicitly specify that it should only be applied to lists where the first element is neither an a, nor a b, nor a c. So, when Prolog is asked to look for alternative solutions, it actually applies this clause to lists which do have an a, or a b, or a c as first element. So, we have to tell Prolog that it shouldn't be doing that. Here is the improved version of the fourth clause. Remember that \= means does not match.

%% Fourth case: the first element is something else.
replace_a_b_c([X|InTail],[X|OutTail]) :-
              X \= a, X \= b, X \= c,
              replace_a_b_c(InTail,OutTail).
    
Download the improved knowledge base here.

Back to the exercise.