簡體   English   中英

計數出現Prolog

[英]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 = bX = 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 ) ,變量XY是統一的,在那個時間點最終決定是否去找Then_0Else_0 如果變量沒有充分實例化,那該怎么辦? 好吧,那么我們運氣不好,只能堅持使用Then_0得到一些隨機結果。

將其與if_( If_1, Then_0, Else_0)對比。 這里,第一個參數必須是一個目標,它將在其最后一個參數中描述Then_0Else_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).

該定義需要以下輔助定義: if_/3equal_truth/3foldl/4

如果您使用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/3findall/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是一個列表,其長度等於ListX的出現次數:

?- 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是一種通常提供多種“正確”方法來解決問題的語言。 如果您在答案中堅持任何順序,那么您的回答並不清楚。 所以,忽略秩序,一種方法是:

  1. 使用穩定排序(不丟棄重復項)排序列表
  2. 在排序列表上應用行程編碼

這種方法的主要優點是它可以將您的問題解構為兩個明確定義(和已解決)的子問題。

第一個很簡單: 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM