4

This is my implementation:

popped_last_el([], [_]).
popped_last_el([F | R], [F | X]) :- popped_last_el(R, X).

last_el(X, [X]).
last_el(X, [_ | L]) :- last_el(X, L).

reverse_list([], []).
reverse_list([F | R], L2) :- last_el(F, L2), popped_last_el(L, L2), reverse_list(R, L).

When I query reverse_list([a, b], X), it outputs X = [b, a] as expected. But when I ask for another solution using ;, prolog runs indefinitely. Why?

false
  • 10,264
  • 13
  • 101
  • 209
BonkClout
  • 43
  • 2

1 Answers1

2

Why?

Here is a to explain why the program still loops. Only this very tiny part of your program is responsible for non-termination. You will have to fix something in that part to make non-termination go away.

last_el(X, [X]) :- false.
last_el(X, [_ | L]) :- last_el(X, L), false.

reverse_list([], []) :- false.
reverse_list([F | R], L2) :-
   last_el(F, L2), false,
   popped_last_el(L, L2),
   reverse_list(R, L).

?- reverse_list([a, b], X), false.
   loops.

What you can see from this failure-slice:

L2 needs to be known ('instantiated') to make last_el/2 terminate. But it is not.

false
  • 10,264
  • 13
  • 101
  • 209
  • 1
    How did you know where to slice/put the falses or is it just trial and error until the script doesn't terminate? Also I can't find a way to fix the problem. Is this approach a lost cause? – BonkClout Apr 17 '22 at 07:44
  • 2
    The following always holds: **if** a fragment does not terminate, then the entire program does not terminate. Some of that can be determined quite easily. And some need to be tried out. (I, in this case, just tried this one first.) See [tag:failure-slice] for more. – false Apr 17 '22 at 08:21
  • 2
    *Is this approach a lost cause?* there are better ways. You can (with low effort) write a pure version that terminates for both `reverse_list([a,b],Xs)` **and** `reverse_list(Ys,[a,b])`. You should really try this yourself (but you find it also here on SO...). – false Apr 17 '22 at 08:23