在Prolog中的元组列表中按降序排序

时间:2017-12-14 10:23:04

标签: list sorting prolog

我想基于列表的第二个值按降序对元组列表进行排序,而不使用内置的排序谓词。

示例:(Name, Age)

unsorted_list = [(mary, 20), (jack, 50), (bob, 16), (bill, 20) ].

sorted_list = [(jack, 50), (bill, 20), (mary, 20), (bob, 16)].

有谁知道这样做的优雅方式?

2 个答案:

答案 0 :(得分:7)

有很多方法可以实现这一点。一种方法是从头开始实现您的自定义对排序,这样做的好处是您可以确切地知道算法的实现,从而确保复杂性,但这可能不是那么优雅。

另一个更优雅的方法是使用Prolog的ISO谓词 keysort/2 ,它对[X1-Y1, X2-Y2, ...]形式的对的列表进行排序,并且排序基于第一个参数对(Xi)。因此,您需要更改[-20-mary, -50-jack, -16-bob, -20-bill]格式的列表,然后应用keysort/2。请注意,由于@ false的注释为了实现降序的稳定解决方案,我们可以否定数字并按升序排序否定数字:

:-use_module(library(clpfd)).

swap_internals((X,Y), Y1-X):- Y1 #= -Y.

pair_sort(L,Sorted):- 
      maplist(swap_internals, L, L2),
      keysort(L2, L3),
      maplist(swap_internals, Sorted, L3).

在上文中,您使用maplist/3swap_internals/2以适当的形式构建列表,并使用keysort/2对其进行排序,然后再次使用maplist/3进行更改。

示例:

   ?- pair_sort([(mary, 20), (jack, 50), (bob, 16), (bill, 20) ],L).
    L = [(jack, 50),  (bill, 20),  (mary, 20),  (bob, 16)].

这是降序的另一种方式,只需使用 sort/4 谓词:

sort(2, @>=, L, Sorted).

示例:

?- sort(2,@>=, [(mary, 20), (jack, 50), (bob, 16), (bill, 20) ], L).
L = [(jack, 50),  (mary, 20),  (bill, 20),  (bob, 16)].

虽然它不是ISO谓词,但这种方式显然更容易。

修改

我没有看到“不使用内置排序谓词”部分问题,因此您可以编写一个典型的合并排序来更改下面merge/2的第3个规则以处理对:< / p>

halve([], [], []).
halve([A], [A], []).
halve([A,B|Cs], [A|X], [B|Y]) :- halve(Cs, X, Y).

merge([], Ys, Ys).
merge(Xs, [], Xs).
merge([(X1,X2)|Xs], [(Y1,Y2)|Ys], M) :-
   (  
      X2 > Y2 -> M = [(X1,X2)|Ms], merge(Xs, [(Y1,Y2)|Ys], Ms) 
    ; M = [(Y1,Y2)|Ms], merge([(X1,X2)|Xs], Ys, Ms) 
   ).

mergeSort([], []).
mergeSort([E], [E]).
mergeSort([E1,E2|Es], SL) :- 
     halve([E1,E2|Es], L1, L2),
     mergeSort(L1, SL1),
     mergeSort(L2, SL2),
     merge(SL1, SL2, SL). 

示例:

?- mergeSort([(mary, 20), (jack, 50), (bob, 16), (bill, 20) ],L).
L = [(jack, 50),  (bill, 20),  (mary, 20),  (bob, 16)] ;
false.

答案 1 :(得分:1)

虽然在原始帖子中请求了非内置实现,但我还是想添加@coder发布的评论,如果你使用的是Swi-Prolog,那么很少有额外的排序谓词:predsort/3sort/4

predsort/3特别有意义,因为它允许您指定自定义排序谓词:

compare_tuples_descending('<', (_, X), (_, Y)) :- X > Y, !.
compare_tuples_descending('>', _, _).

sort_tuples_descending(Unsorted, Sorted) :-
    predsort(compare_tuples_descending, Unsorted, Sorted).

用法示例:

?- sort_tuples_descending([(mary, 20), (jack, 50), (bob, 16), (bill, 20)], S).
S = [(jack, 50), (bill, 20), (mary, 20), (bob, 16)].