Search code examples
prologinfinite-loopmergesortfailure-slice

Prolog Implementation of Mergersort won't halt


I am a new Prolog developer and am trying to get merge sort working.The query

mergesort([2,1],T).

Will Produce

T=[1,2];
T=[1,2];
T=[1,2];
...

So Although it seems to "correctly" sort the sequence it doesn't halt

On the other hand if I had a query such as

mergesort([2,1],[2,1])

It gets into an infinite loop. I am wondering why this is the case?

append([H|T],LISTB,[H|LISTC]):-
    append(T,LISTB,LISTC).

split(LIST,L1,L2):-
    length(LIST,LENGTH),
    M is div(LENGTH,2),
    append(L1,L2,LIST),
    length(L1,L1LENGTH),
    (L1LENGTH =:= M).

merge(A,[],A).
merge([],B,B).
merge([A|TA],[B|TB],[A|MERGED]) :-
    A =< B,
    merge(TA,[B|TB],MERGED).
merge([A|TA],[B|TB],[B|MERGED]) :-
    B < A,
    merge([A|TA],TB,MERGED).

mergesort([],[]).
mergesort([X],[X]).
mergesort(LIST,OLIST):-
    split(LIST,L1,L2),
    mergesort(L1,OLIST1),
    mergesort(L2,OLIST2),
    merge(OLIST1,OLIST2,OLIST).

Solution

  • (append([], L, L). was missing)

    First, reduce the size of your input! The problem you get with [2,1] can already be observed with [2] and even []!

    ?- mergesort([],T).
       T = []
    ;  T = []
    ;  T = []
    ;  T = []
    ;  ... .
    

    So the problem seems to be that the program does not terminate. Or maybe it stops after ten answers? There is a way to better test this:

    ?- mergesort([], T), false.
       loops.
    

    And if I am at it, I will add further goals into your program such that the resulting program still loops:

    append([], L, L).
    append([H|T],LISTB,[H|LISTC]):- false,
        append(T,LISTB,LISTC).
    
    split(LIST,L1,L2):- LIST = [],
        length(LIST,LENGTH),
        M is div(LENGTH,2),
        append(L1,L2,LIST),
        length(L1,L1LENGTH),
        (L1LENGTH =:= M).
    
    mergesort([],[]) :- false.
    mergesort([X],[X]) :- false.
    mergesort(LIST,OLIST):- LIST = [],
        split(LIST,L1,L2), L1 = [], L2 = [],
        mergesort(L1,OLIST1), false,
        mergesort(L2,OLIST2),
        merge(OLIST1,OLIST2,OLIST).

    I have added goals false and L = [] which all terminate and thus will either improve termination or leave it as is. This resulting fragment of the program is known as a .

    Because this program loops, also your original program loops. Thus, there must be an error somewhere in the visible part. Does it really make sense that the recursive rule of mergesort/2 applies to the empty list?

    ((Apart from that, split is quite inefficient as it stands.))