【问题标题】:Sorting a list in prolog based on 2 keys根据 2 个键对序言中的列表进行排序
【发布时间】:2020-12-12 12:12:23
【问题描述】:

假设我有这个列表。

A=[(a,1), (b,3), (d,2), (c,2)]

我想根据两件事对其进行排序,首先我检查数字,如果它们相同,我需要按字母排序,例如这将被排序为:

S=[(a,1), (c,2), (d,2), (b,3)]

如果我用这个

sort(2, @=<, A, S).

我得到的是排序但仅基于数字的列表,如何添加该加号控件?

【问题讨论】:

  • 你有predsort/3吗?
  • @DavidTonhofer 我不认为 predsort/3 是正确的。能给我举个例子吗。相反,order_by/2 是我会使用的。 Order_by/2 examples
  • @DavidTonhofer 我找到了您的示例,虽然它会起作用,但恕我直言,这比必要的工作多。 lettersnumbers 这两种不同的类型已经有了排序的方法,所以为什么不使用它呢。

标签: sorting prolog key


【解决方案1】:

一些替代方案:

sort1(L , S) :-
    predsort([R,(A,B),(C,D)]>>compare(R,(B,A),(D,C)), L, S).

sort2(L, S) :-
    findall((A,B), order_by([asc(B),asc(A)], member((A,B),L)), S).

sort3(L, S) :-
    maplist([(X,Y),(Y,X)]>>true, L, M),
    msort(M, M1),
    maplist([(X,Y),(Y,X)]>>true, M1, S).

一些例子:

?- A=[(a,1), (b,3), (d,2), (a,0), (c,2), (b,3)], time(sort1(A,S)).
% 53 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
A = [(a, 1),  (b, 3),  (d, 2),  (a, 0),  (c, 2),  (b, 3)],
S = [(a, 0),  (a, 1),  (c, 2),  (d, 2),  (b, 3)].

?- A=[(a,1), (b,3), (d,2), (a,0), (c,2), (b,3)], time(sort2(A,S)).
% 100 inferences, 0.000 CPU in 0.001 seconds (0% CPU, Infinite Lips)
A = [(a, 1),  (b, 3),  (d, 2),  (a, 0),  (c, 2),  (b, 3)],
S = [(a, 0),  (a, 1),  (c, 2),  (d, 2),  (b, 3),  (b, 3)].

?- A=[(a,1), (b,3), (d,2), (a,0), (c,2), (b,3)], time(sort3(A,S)).
% 29 inferences, 0.000 CPU in 0.000 seconds (?% CPU, Infinite Lips)
A = [(a, 1),  (b, 3),  (d, 2),  (a, 0),  (c, 2),  (b, 3)],
S = [(a, 0),  (a, 1),  (c, 2),  (d, 2),  (b, 3),  (b, 3)].

庞大列表的执行时间:

?- N is 10**6, 
   length(L, N), 
   maplist([(X,Y)]>>(random(1,N,X), random(1,N,Y)), L), 
   time(sort1(L,A)), 
   time(sort2(L,B)), 
   time(sort3(L,C)), 
   fail.

% 76,846,271 inferences, 13.063 CPU in 13.171 seconds (99% CPU, 5882968 Lips)
% 4,000,076 inferences, 3.063 CPU in 3.140 seconds (98% CPU, 1306147 Lips)
% 4,000,005 inferences, 2.375 CPU in 2.375 seconds (100% CPU, 1684213 Lips)
false.

似乎最好将maplist/3msort/2结合使用。

【讨论】:

  • 如果您根据推理次数做出“最佳”判断,那就是误导。 SWI-Prolog 中的排序是在 C 中实现的,因此不计算任何推论。
  • 啊,我没想过实际上对 predsort 本身进行 lambda 化。不错。
  • 感谢发帖。现在我不用写答案了。
  • @IsabelleNewbie:确实如此,但是对于第三种选择,总体执行时间仍然更好。
  • 有趣,在我的 SWI 7.6.4 机器上 sort2 需要 2 秒,sort3 需要 10 秒。看起来这个版本的 lambdas 很慢。
【解决方案2】:

根据sort/4'@=

如果我们接受重复消除,我们可以调整元谓词 predsort/3 以适应我们的需要。

predsort(+Pred, +List, -Sorted)

这只是其他编程语言中已知的“排序方法”的另一种形式。例如,在 Java 中,Collections.sort 采用 Comparator 接口。这里,predsort/3 将谓词名称 Pred(或部分填充的谓词调用)作为可调用过程。一样的。

谓词compare/3 正是我们需要的元素比较,因为它使用standard order of terms 比较整个术语:

?- compare(D,(a,1),(b,3)).
D =  (<).

?- compare(D,(a,1),(a,3)).
D =  (<).

?- compare(D,(a,3),(a,1)).
D =  (>).

?- compare(D,(b,3),(a,1)).
D =  (>).

但是,它首先按第一个子元素进行比较,然后按第二个子元素进行比较(或者更准确地说,按仿函数名称 , 的复合项的参数顺序)

所以我们必须先扭转这些。这是通过另一个元谓词maplist/3 完成的,其中“将一个列表的元素映射到另一个列表的元素的谓词”将是“反转的谓词”。由于我懒得为此添加单独的谓词,我将使用library(yall) 将其写为“内联”:[(C,N),(N,C)]&gt;&gt;true 相当于reverse((C,N),(N,C))

所以:

my_sort(ListIn,ListOut) :-
   maplist([(C,N),(N,C)]>>true,ListIn,List2),
   predsort(compare,List2,List3),
   maplist([(C,N),(N,C)]>>true,ListOut,List3).

所以:

?- my_sort([(a,1), (b,3), (d,2), (c,2)], Out).
Out = [(a, 1),  (c, 2),  (d, 2),  (b, 3)].

请注意,将(A,B) 结构成对使用并不完美——, 是连词。改用实际的对:A-B(即真正的 -(A,B) 结构)。

它适用于空列表吗?是的。

?- my_sort([],[]).
true.

这里有更完整的例子:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-23
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    相关资源
    最近更新 更多