Reputation: 31
I'm attempting to sort a list of colors, by a given preffered order. For example a list [r,z,z,w,g,g,r,z] sorted in this order [z,b,g,r,w], will give an end result of [z,z,z,g,g,r,r,w].
I tried using a basic bubblesort algorithme and adding a check to see which of first two terms would be 'higher' on the order list.
% take the to-sorted list, the order in which to sort the list, and the
% result.
%colourSort([r,z,z,w,g,g,r,z],[z,b,g,r,w],X). returns X = [z,z,z,g,g,r,r,w]
colourSort(List,Order,Sorted):-
swap(List,List1,Order),
!,
colourSort(List1,Order,Sorted).
colourSort(Sorted,_,Sorted).
% check if the either the first or second letter is first in the order
% list, if neither check the next letter in the order list.
check(A,_,[H|_],A):-
A == H.
check(_,B,[H|_],B):-
B == H.
check(A,B,[_|T],R):-
check(A,B,T,R).
check(_,_,[],_).
%swap incase a set of letters isn't ordered, continues otherwise.
swap([X,Y|Rest],[Y,X|Rest],Order):-
check(X,Y,Order,R),
X == R.
swap([Z|Rest],[Z|Rest1],Order) :-
swap(Rest,Rest1,Order).
When I run the code, it ends up crashing my swi-prolog, I'm assuming it's getting stuck in a loop or something, but haven't been able to figure out why or how.
Any advice or tips would be appreciated.
Upvotes: 3
Views: 888
Reputation: 4098
Here's a solution to the stated problem, which does not, however, use a custom sorting algorithm. Instead, it uses the common pairs data-structure (using the (-)/2
operator to form a list of items Key-Value
) and the keysort/2
for sorting. Edit: this answer has been reworked in accordance with @mat's tip in the comments, and to provide a more succinct explanation).
Solution:
item_with_rank(Ranking, Item, Rank-Item) :-
nth0(Rank, Ranking, Item).
sort_by_ranking(Ranking, ToSort, Sorted) :-
maplist(item_with_rank(Ranking), ToSort, Ranked),
keysort(Ranked, RankedSorted),
pairs_values(RankedSorted, Sorted).
Explanation:
We define a predicate item_with_rank(Ranking, Item, Rank-Item)
that uses a list of arbitrarily ordered terms as a Ranking
, and associates with the given Item
a Rank
which is equivalent to the 0-based index of the first term in Ranking
that unifies with Item
. We then define sort_by_ranking(Ranking, ToSort, Sorted)
. sort_by_ranking/3
uses maplist/3
to call item_with_rank/3
, with the given Ranking
, on each element of the list ToSort
, obtaining a list of pairs, Ranked
, assigning a rank to each item. We use keysort/2
to sort the Ranked
so that they order of elements accords with the value of their "ranks" (keys) in RankedSorted
. When we extract just the values from RankedSorted
, we are left with the Sorted
items, which is what we were after:
Example of usage:
?- sort_by_ranking([z,b,g,r,w], [r,z,z,w,g,g,r,z], S).
S = [z, z, z, g, g, r, r, w] ;
false.
Upvotes: 3