Search code examples
prologfailure-slice

Prolog: Trouble sorting a list


I have a list like this that I am trying to sort:

[(tim,3),(tom,4),(jane,2),(mary,3)]

I want to rearrange it so it orders in descending numbers:

[(tom,4),(mary,3),(tim,3),(jane,2)]

I have a predicate that extract a list for a given number:

extractor([],[],[],_).
extractor([(Name, Number)|List], [(Name, Number)|NewList], Extracted, Number):-
   extractor(List, NewList, Extracted, Number),
   !.
extractor([A|List], NewList,[A|Extracted], Number):-
   extractor(List, NewList, Extracted, Number).

So given a number it should give me a list of the elements with those numbers and have an extracted list without those elements.

Then I put that through the sort. Where I have a highest number, and it should loop through from 0 to that number, extracting any elements for that number. Until I have the sorted list.

numberSort([], _, 0).
numberSort(Unsorted, Sorted, HighestNumber):-
   extractor(Unsorted, Sorted, ExtractedList, Counter),
   numberSort(ExtractedList, Sorted, Counter),
   HighestNumber is 1 + Counter.
numberSort(Unsorted, Sorted, HighestNumber):-
   numberSort(Unsorted, Sorted, Counter),
   HighestNumber is 1 + Counter.

However, when I try this it just goes through an infinite loop. Can someone help me out and show me where I am going wrong? Thanks,


Solution

  • First of all, before looking at your code in detail, lets make clear what the relation should describe. You have pairs of the form (Name, Number) — as an aside, usually Prolog code prefers to write Name-Number instead. And you want a relation between two list of pairs, the second being a permutation with pairs descending by number.

    What about negative numbers? It seems you are not considering such a case. At least, 0 occurs in your program which suggests that you intended it to be the highest number, should list be [].

    Why does your program loop? You might want to go through a debugger to see what "happens" step-by-step. Well, why not? Try it out! You will see that you see a lot of unrelated stuff happening. More effectively is to use a like so:

    numberSort([], _, 0) :- false.
    numberSort(Unsorted, Sorted, HighestNumber) :- false,
       extractor(Unsorted, Sorted, ExtractedList, Counter),
       numberSort(ExtractedList, Sorted, Counter),
       HighestNumber is 1 + Counter.
    numberSort(Unsorted, Sorted, HighestNumber) :-
       numberSort(Unsorted, Sorted, Counter), false,
       HighestNumber is 1 + Counter.
    
    ?- numberSort( [(tim,3),(tom,4),(jane,2),(mary,3)], Sorted, H).
       loops.
    

    This fragment of your program still loops, and therefore also your original program loops. You do not need to see more than that! Worse: Your program loops for all queries! To fix this problem you have to fix at least something in the remaining visible part. That might look surprising to you after all you spent some time with extractor/4 but regardless of its definition, this loop occurs.

    The other issue is with extractor/4:

    ?- extractor([(tim,3),(jane,4)], Xs, Ys, 3).
       Xs = [(tim,3)], Ys = [(jane,4)].
    ?- extractor([(tim,3),(jane,4)], [], Ys, 3).
       Ys = [(tim,3),(jane,4)].
    

    Isn't this odd? The first query succeeds (probably according to you expectations), the second should thus fail, but it succeeds. The culprit being the cut which you placed at the end of the rule. We say, the predicate is not steadfast. I'd suggest to write this predicate in a manner that you do not need any cut at all.