[英]Prolog - How to sort a list of lists containing numbers by the sum of the numbers in each list
假設我有一個看起來像這樣的列表:
S1 = [[3,6],[1,3],[4,12],[10]]
我希望排序列表為:
S = [[1,3],[3,6],[10],[4,12]](4 <9 <10 <16)
更新:我想我想出了一個使用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).
為什么要使用緩慢且不可攜帶的predsort/3
? 改用keysort/2
雙贏!
每個與 ISO兼容的Prolog系統都提供keysort/2
不同於predsort/3
或msort/2
。
使用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).
OP給出的示例查詢:
| ?- zss_sumsorted([[3,6],[1,3], [4,12],[10]], Xss).
Xss = [[1,3],[3,6],[10],[4,12]] ? ;
no
編輯: @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)
另一個版本,基於與@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).
主要區別是使用了keysort / 2而不是msort / 2,因為后者也將實際列表納入排序,而keysort / 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]]
與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]]
原始代碼有幾個問題。 (1) A
和B
實際上是你原來的列表子列表的頭,讓你真正想sumlist
的[A|C]
和[B|D]
而不是A
和B
。 您的邏輯( ;
)邏輯將意味着只有在X = Y
為true時才調用compare/3
。 (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
列表,其中S
是L
的總和 msort
排序將通過Prolog中的“自然”順序出現的SL
術語,並將最小的S
放在第一位 SL
排序列表映射回僅L
的列表 實現看起來像這樣:
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]].
之所以可行,是因為Prolog將使用術語比較(請參見文檔中的@<
, @>
等)進行排序。 如果A
小於Y
則AB
小於XY
。 因此,將9-[3,6]
與10-[10]
會發現9-[3,6]
較小。 比較16-[4,12]
和10-[10]
會發現16-[4,12]
更大。
keysort/2
在這里代替msort/2
更加規范和可移植。
您的解決方案似乎錯了:可能是
mycompare(C, A, B) :-
sumlist(A, Sa), sumlist(B, Sb),
( Sa < Sb -> C = < ; C = > ).
lol_sorted(L, S) :-
predsort(mycompare, L, S).
或者,或者:
lol_sorted(L, S) :-
setof(N-E, (member(E,L),sumlist(E,N)), R),
pairs_values(R, S).
這將丟失重復的相同列表(而不是總和)。 要保留它們,請使用稍微復雜一點的代碼段:
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.