[英]Prolog - How to sort a list of lists containing numbers by the sum of the numbers in each list
Assume I have a list that looks like this one: 假设我有一个看起来像这样的列表:
S1 = [ [3, 6], [1, 3], [4, 12], [10] ] S1 = [[3,6],[1,3],[4,12],[10]]
And I want the sorted list to be: 我希望排序列表为:
S = [ [1, 3], [3, 6], [10], [4, 12] ] ( 4 < 9 < 10 < 16 ) S = [[1,3],[3,6],[10],[4,12]](4 <9 <10 <16)
Update: I think I come up with a solution using predsort/3 and sumlist/2. 更新:我想我想出了一个使用predsort / 3和sumlist / 2的解决方案。
mycompare(Comp, [A|C], [B|D]):-
sumlist(A, X), sumlist(B, Y),
( X < Y -> Comp = '<'
; X > Y -> Comp = '>'
; compare(Comp, [A|C], [B|D])).
predsort(mycompare, S1, S).
Why use the slow and non-portable predsort/3
? 为什么要使用缓慢且不可携带的
predsort/3
? Use keysort/2
instead—it's win-win! 改用
keysort/2
双赢!
Every ISO-compliant Prolog system provides keysort/2
—unlike predsort/3
or msort/2
... 每个与 ISO兼容的Prolog系统都提供
keysort/2
不同于predsort/3
或msort/2
。
Using SICStus Prolog 4.3.2 we define zss_sumsorted/2
like so: 使用SICStus Prolog 4.3.2,我们定义
zss_sumsorted/2
像这样:
:- use_module(library(lists), [maplist/3,sumlist/2,keys_and_values/3]). :- use_module(library(types), [must_be/4]). zss_sumsorted(L0, L) :- must_be(L0, list(list(integer)), zss_sumsorted(L0,L), 1), maplist(sumlist, L0, L1), keys_and_values(L10, L1, L0), keysort(L10, L10_sorted), keys_and_values(L10_sorted, _, L).
Sample query as given by the OP: OP给出的示例查询:
| ?- zss_sumsorted([[3,6],[1,3], [4,12],[10]], Xss).
Xss = [[1,3],[3,6],[10],[4,12]] ? ;
no
Edit: @tas suggested some more test cases with non-ground instantiation, which should raise the right exceptions—with a little help by must_be/4
. 编辑: @tas建议使用非地面实例化的更多测试用例,这应该引发正确的异常—在
must_be/4
帮助下。
| ?- zss_sumsorted([[3,6],[1,3],[4,12],[10],[_]], Xss). ! Instantiation error in argument 1 of user:zss_sumsorted/2 ! goal: zss_sumsorted([[3,6],[1,3],[4,12],[10],[_229]],_179) | ?- zss_sumsorted([[3,6],[1,3],[4,12],[10],x], Xss). ! Type error in argument 1 of user:zss_sumsorted/2 ! expected list of an integer, but found x ! goal: zss_sumsorted([[3,6],[1,3],[4,12],[10],x],_237)
Yet another version, based on the same idea as suggested by @lurker, but without the elegance of maplist/3: 另一个版本,基于与@lurker建议的相同思想,但没有maplist / 3的优雅之处:
:- use_module(library(clpfd)).
:- use_module(library(pairs)).
lists_sorted(L,S) :-
lists_withsums(L,SL),
keysort(SL,SSL), % instead of msort/2
pairs_values(SSL,S).
lists_withsums([],[]).
lists_withsums([List|Ls], [Sum-List|SLs]) :-
sum(List,#=,Sum),
lists_withsums(Ls,SLs).
The main difference is the use of keysort/2 instead of msort/2 as the latter also incorporates the actual list in the sorting whereas keysort/2 retains the order in which the lists with equal sums occur. 主要区别是使用了keysort / 2而不是msort / 2,因为后者也将实际列表纳入排序,而keysort / 2保留了总和相等的列表出现的顺序。 Example query with msort/2:
使用msort / 2的示例查询:
?- lists_sorted([[3,6],[3,1],[1,3],[4,12],[10],[2,2],[1,3]], L).
L = [[1,3],[1,3],[2,2],[3,1],[3,6],[10],[4,12]]
Same query with keysort/2: 与keysort / 2相同的查询:
?- lists_sorted([[3,6],[3,1],[1,3],[4,12],[10],[2,2],[1,3]], L).
L = [[3,1],[1,3],[2,2],[1,3],[3,6],[10],[4,12]]
Several problems with the original code. 原始代码有几个问题。 (1)
A
and B
are actually the head of sublists of your original list, so you really wanted sumlist
of [A|C]
and [B|D]
, not of A
and B
. (1)
A
和B
实际上是你原来的列表子列表的头,让你真正想sumlist
的[A|C]
和[B|D]
而不是A
和B
。 Your disjunction ( ;
) logic will mean that compare/3
is only called if X = Y
is true. 您的逻辑(
;
)逻辑将意味着只有在X = Y
为true时才调用compare/3
。 (3) The comparator is being used to compare the lists themselves, which will fail. (3)使用比较器来比较列表本身,这将失败。
mycompare(Comp, [A|C], [B|D]):-
sumlist(A, X), sumlist(B, Y), % ERROR: A and B are the heads of the list *element*
% you really want sumlist([A|C], X), etc...
( X < Y -> Comp = '<' % Choosing comparator based upon sum
; X > Y -> Comp = '>' % ERROR: Your disjunction (;) will ONLY do the
% following compare if `X = Y`
; compare(Comp, [A|C], [B|D])). % ERROR: comparing two lists with comparator
SL
where S
is the sum of L
SL
列表,其中S
是L
的总和 msort
to sort SL
terms which will occur via the "natural" order in Prolog and put smallest S
first msort
排序将通过Prolog中的“自然”顺序出现的SL
术语,并将最小的S
放在第一位 SL
back to a list of just L
SL
排序列表映射回仅L
的列表 The implementation looks like this: 实现看起来像这样:
sort_by_sum(InputList, SortedList) :- maplist(pre_sum, InputList, SumWithList), msort(SumWithList, SumWithListSorted), maplist(un_sum, SumWithListSorted, SortedList). pre_sum(L, SL) :- sumlist(L, S). un_sum(_-L, L). ?- sort_by_sum([ [3, 6], [1, 3], [4, 12], [10] ], L). L = [[1, 3], [3, 6], [10], [4, 12]].
This works because Prolog will use term comparisons (see @<
, @>
, etc in the documentation) in order to do the sort. 之所以可行,是因为Prolog将使用术语比较(请参见文档中的
@<
, @>
等)进行排序。 A term such as AB
is less than XY
if A
is less than Y
. 如果
A
小于Y
则AB
小于XY
。 So comparing 9-[3,6]
with 10-[10]
will find that 9-[3,6]
is less. 因此,将
9-[3,6]
与10-[10]
会发现9-[3,6]
较小。 And comparing 16-[4,12]
with 10-[10]
will find that 16-[4,12]
is greater. 比较
16-[4,12]
和10-[10]
会发现16-[4,12]
更大。
keysort/2
would be more canonical and portable here in place of msort/2
.
keysort/2
在这里代替msort/2
更加规范和可移植。
your solution seems wrong: could be 您的解决方案似乎错了:可能是
mycompare(C, A, B) :-
sumlist(A, Sa), sumlist(B, Sb),
( Sa < Sb -> C = < ; C = > ).
lol_sorted(L, S) :-
predsort(mycompare, L, S).
or, alternatively: 或者,或者:
lol_sorted(L, S) :-
setof(N-E, (member(E,L),sumlist(E,N)), R),
pairs_values(R, S).
this will loose duplicated, identical lists (not sums). 这将丢失重复的相同列表(而不是总和)。 To preserve them, a slightly more complex snippet:
要保留它们,请使用稍微复杂一点的代码段:
lol_sorted(L, S) :-
setof(N/P-E, (nth0(P,L,E),sumlist(E,N)), R),
pairs_values(R, S).
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.