While in other programming languages thinking in terms of numbers would be good idea, in Prolog however thinking in terms of numbers or counts is not always the best way. What I personally find to be a better way is to think in term of parts of graphs or symbols.
Graph
From: GeeksForGeeks
Symbols
?- write_term([1,2,3],[dotlists(true)]).
.(1,.(2,.(3,[])))
true.
To help you better understand how to think about list here are few examples.
Print a list with recursion, which is to walk a list and print each item when visited.
print_list([]) :-
format('~n',[]).
print_list([H|T]) :-
format('~w ',H),
print_list(T).
Example runs:
?- print_list([1,2,3]).
1 2 3
true.
?- print_list([1]).
1
true.
?- print_list([]).
true.
So now you know how to walk a list using recursion.
The next challenge is to change the value of a list. Remember that in Prolog values are immutable so to change a list you need two variables, one for the original list and one for the changed list.
First example will just walk a list and duplicate it.
duplicate_list([],[]).
duplicate_list([H0|T0],[H0|T]) :-
duplicate_list(T0,T).
?- duplicate_list([1,2,3],L).
L = [1, 2, 3].
?- duplicate_list([1],L).
L = [1].
?- duplicate_list([],L).
L = [].
Next we will reverse a list. This needs a helper predicate, but instead of adding the word _helper
to the name, because the helper uses 3 arguments and the query predicate uses 2 arguments, the same name can be used for both.
This is not needed to solve your question, but you should be aware of it as it looks like other recursive code, but is dramatically different. In other words, the construction of the list is not done in the head of the clause, but in the body of the clause.
reverse_list(List,Rev) :-
reverse_list(List,Rev,[]).
reverse_list([],L,L).
reverse_list([H|T],L,Rest) :-
reverse_list(T,L,[H|Rest]).
?- reverse_list([1,2,3],R).
R = [3, 2, 1].
?- reverse_list([1],R).
R = [1].
?- reverse_list([],R).
R = [].
Now to learn how to choose the last item in the list.
It is common for recursion routines to stop at the end of the structure, but in this case you don’t want to stop at the end, you want to stop at 1 less than the end.
The predicate used to stop the recursion at the end of the structure is like
duplicate_list([],[]).
but to stop one short of the end it would be like
duplicate_list([H|[]],[H|[]]).
and to remove the last item would be the same as starting the new list without the last item which was H
duplicate_list([H|[]],[]).
which is the same as
duplicate_list([_],[]).
So to remove the last item it is
remove_last_list([_],[]).
remove_last_list([H0|T0],[H0|T]) :-
remove_last_list(T0,T).
Examples
Note that it leaves a choicepoint, but don’t want to teach about cuts in this topic.
?- remove_last_list([1,2,3],L).
L = [1, 2] ;
false.
?- remove_last_list([1],L).
L = [] ;
false.
?- remove_last_list([],L).
false.
I suspect that because you used append/3
in your question you are doing an exercise that ask you to use append/3. I know this did not use append/3, but it should help you.
Also of help to know when working with list in Prolog
?- write_term([1,2,3],[dotlists(true)]).
.(1,.(2,.(3,[])))
true.
?- write_term([1],[dotlists(true)]).
.(1,[])
true.
?- write_term([],[dotlists(true)]).
[]
true.
?- [_] = [_|[]].
true.
?- write_term([_],[dotlists(true)]).
.(_2178,[])
true.
?- write_term([_|[]],[dotlists(true)]).
.(_2404,[])
true.