[英]Count occurrences Prolog
我是Prolog的新手,並試圖用Lists做一些編程
我想做這個 :
?- count_occurrences([a,b,c,a,b,c,d], X).
X = [[d, 1], [c, 2], [b, 2], [a, 2]].
這是我的代碼我知道它不完整但我正在嘗試:
count_occurrences([],[]).
count_occurrences([X|Y],A):-
occurrences([X|Y],X,N).
occurrences([],_,0).
occurrences([X|Y],X,N):- occurrences(Y,X,W), N is W + 1.
occurrences([X|Y],Z,N):- occurrences(Y,Z,N), X\=Z.
我的代碼是錯的,所以我需要一些點擊或幫助PLZ ..
請注意,到目前為止,所有提案都存在包含變量的列表的困難。 想想這個案子:
?- count_occurrences([a,X], D).
應該有兩個不同的答案。
X = a, D = [a-2] ;
dif(X, a), D = [a-1,X-1].
第一個答案是指:該列表[a,a]
包含a
兩次,因此D = [a-2]
第二個答案涵蓋了與a
不同的所有術語X
,對於那些,我們有一次出現a
,另一次出現另一個術語。 請注意,第二個答案包括無限可能的解決方案,包括X = b
或X = c
或您想要的任何其他解決方案。
如果實現無法產生這些答案,則實例化錯誤應該保護程序員免受進一步損害。 一些東西:
count_occurrences(Xs, D) :-
( ground(Xs) -> true ; throw(error(instantiation_error,_)) ),
... .
理想情況下,Prolog謂詞被定義為純粹的關系, 就像這個 。 但通常,純粹的定義效率很低。
這是一個純粹而有效的版本。 從高效率的角度來看,它不會留下任何不必要的選擇點。 我把@dasblinkenlight的定義作為靈感來源。
理想情況下,此類定義使用某種形式的if-then-else。 但是,傳統的(;)/2
寫的
( If_0 -> Then_0 ; Else_0 )
是一種固有的非單調結構。 我將使用單調的對應物
if_( If_1, Then_0, Else_0)
代替。 主要區別在於條件。 傳統的控制結構依賴於If_0
的成功或失敗,它破壞了所有的純度。 如果你寫( X = Y -> Then_0 ; Else_0 )
,變量X
和Y
是統一的,在那個時間點最終決定是否去找Then_0
或Else_0
。 如果變量沒有充分實例化,那該怎么辦? 好吧,那么我們運氣不好,只能堅持使用Then_0
得到一些隨機結果。
將其與if_( If_1, Then_0, Else_0)
對比。 這里,第一個參數必須是一個目標,它將在其最后一個參數中描述Then_0
或Else_0
是否是這種情況。 如果目標尚未確定,它可以選擇兩者 。
count_occurrences(Xs, D) :-
foldl(el_dict, Xs, [], D).
el_dict(K, [], [K-1]).
el_dict(K, [KV0|KVs0], [KV|KVs]) :-
KV0 = K0-V0,
if_( K = K0,
( KV = K-V1, V1 is V0+1, KVs0 = KVs ),
( KV = KV0, el_dict(K, KVs0, KVs ) ) ).
=(X, Y, R) :-
equal_truth(X, Y, R).
如果您使用SWI-Prolog,您可以:
:- use_module(library(lambda)).
count_occurrences(L, R) :-
foldl(\X^Y^Z^(member([X,N], Y)
-> N1 is N+1,
select([X,N], Y, [X,N1], Z)
; Z = [[X,1] | Y]),
L, [], R).
應該使解決問題更容易的一件事是設計一個輔助謂詞來增加計數。
想象一個謂詞,它帶有一對[SomeAtom,Count]
列表和一個需要遞增計數的原子,並產生一個具有遞增計數的列表,或者為第一次出現的原子生成[SomeAtom,1]
。 這個謂詞很容易設計:
increment([], E, [[E,1]]).
increment([[H,C]|T], H, [[H,CplusOne]|T]) :-
CplusOne is C + 1.
increment([[H,C]|T], E, [[H,C]|R]) :-
H \= E,
increment(T, E, R).
當我們添加第一個匹配項時,第一個子句用作基本情況。 當head元素與所需元素匹配時,第二個子句用作另一個基本情況。 最后一種情況是當head元素與所需元素不匹配時的情況的遞歸調用。
有了這個謂詞,編寫count_occ
變得非常簡單:
count_occ([], []).
count_occ([H|T], R) :-
count_occ(T, Temp),
increment(Temp, H, R).
這是Prolog的普通遞歸謂詞,帶有一個簡單的基本子句和一個處理尾部的遞歸調用,然后使用increment
來計算列表的head元素。
這是我使用bagof/3
和findall/3
解決方案:
count_occurrences(List, Occ):-
findall([X,L], (bagof(true,member(X,List),Xs), length(Xs,L)), Occ).
一個例子
?- count_occurrences([a,b,c,b,e,d,a,b,a], Occ).
Occ = [[a, 3], [b, 3], [c, 1], [d, 1], [e, 1]].
這個怎么運作
對於列表X
每個不同元素, bagof(true,member(X,List),Xs)
是滿足的, Xs
是一個列表,其長度等於List
中X
的出現次數:
?- bagof(true,member(X,[a,b,c,b,e,d,a,b,a]),Xs).
X = a,
Xs = [true, true, true] ;
X = b,
Xs = [true, true, true] ;
X = c,
Xs = [true] ;
X = d,
Xs = [true] ;
X = e,
Xs = [true].
外部findall/3
在表示解決方案的列表中收集元素X
和關聯列表Xs
的長度。
編輯I :得益於CapelliC和Boris的建議,原來的答案得到了改進。
編輯II :如果給定列表中有自由變量,則可以使用setof/3
而不是findall/3
。 setof/3
的問題在於,對於空列表,它將失敗,因此必須引入特殊子句。
count_occurrences([],[]).
count_occurrences(List, Occ):-
setof([X,L], Xs^(bagof(a,member(X,List),Xs), length(Xs,L)), Occ).
你已經得到了答案。 Prolog是一種通常提供多種“正確”方法來解決問題的語言。 如果您在答案中堅持任何順序,那么您的回答並不清楚。 所以,忽略秩序,一種方法是:
這種方法的主要優點是它可以將您的問題解構為兩個明確定義(和已解決)的子問題。
第一個很簡單: msort(List, Sorted)
第二個涉及更多,但如果你希望謂詞只能以一種方式工作,那么仍然是直接的,即List - > Encoding。 一種可能性(非常明確):
list_to_rle([], []).
list_to_rle([X|Xs], RLE) :-
list_to_rle_1(Xs, [[X, 1]], RLE).
list_to_rle_1([], RLE, RLE).
list_to_rle_1([X|Xs], [[Y, N]|Rest], RLE) :-
( dif(X, Y)
-> list_to_rle_1(Xs, [[X, 1],[Y, N]|Rest], RLE)
; succ(N, N1),
list_to_rle_1(Xs, [[X, N1]|Rest], RLE)
).
所以現在,從頂層:
?- msort([a,b,c,a,b,c,d], Sorted), list_to_rle(Sorted, RLE).
Sorted = [a, a, b, b, c, c, d],
RLE = [[d, 1], [c, 2], [b, 2], [a, 2]].
另外,在XN
,選擇“對”幾乎總是更好,而不是像[X, N]
那樣精確地使用兩個元素的列表。 此外,如果您想要正確,您應該保持列表中元素的原始順序。 從這個答案 :
rle([], []).
rle([First|Rest],Encoded):-
rle_1(Rest, First, 1, Encoded).
rle_1([], Last, N, [Last-N]).
rle_1([H|T], Prev, N, Encoded) :-
( dif(H, Prev)
-> Encoded = [Prev-N|Rest],
rle_1(T, H, 1, Rest)
; succ(N, N1),
rle_1(T, H, N1, Encoded)
).
為什么更好?
我們在代碼中刪除了4對不必要的括號
我們在報告的解決方案中擺脫了混亂
我們擺脫了大量不必要的嵌套術語:比較.(a, .(1, []))
-(a, 1)
我們讓程序的意圖更清晰(這是在Prolog中表示對的傳統方式)
從頂層:
?- msort([a,b,c,a,b,c,d], Sorted), rle(Sorted, RLE).
Sorted = [a, a, b, b, c, c, d],
RLE = [a-2, b-2, c-2, d-1].
所提出的行程編碼器在其定義中非常明確,當然它的優點和缺點。 請參閱此答案 ,了解更為簡潔的方法。
精煉joel76 回答 :
count_occurrences(L, R) :-
foldl(\X^Y^Z^(select([X,N], Y, [X,N1], Z)
-> N1 is N+1
; Z = [[X,1] | Y]),
L, [], R).
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.