在序言中获取列表的分布

Get a distribution of a list in prolog

我想在序言中计算手牌的分布。 这意味着得到这个结果:

?- distribution([sq,s9,s8,ha,hk,h5,da,dk,dj,d4,ca,c7,c6],D).
D=[[spade,3],[heart,3],[diamond,4],[club,3]]

但我不知道如何进行。我可以使用以下脚本逐一获取每种颜色:

distribution([], []).
distribution([H|T], [E|D]):-
    atom_chars(H, X),
    nth0(0, X, E),
    ( 
        E == 's' -> distribution(T, D); 
        E == 'h' -> distribution(T, D); 
        E == 'd' -> distribution(T, D); 
        E == 'c' -> distribution(T, D)
    ).

谁能帮帮我?谢谢

一个可能的解决方案是:

distribution(Cards, Distribution) :-
    distribution(Cards, [], Distribution).

distribution([], Distribution, Distribution).
distribution([Card|Cards], Accumulator, Distribution) :-
    suit(Card, Suit),
    update(Accumulator, Suit, NewAccumulator),
    distribution(Cards, NewAccumulator, Distribution).

suit(Card, Suit) :-
    atom_chars(Card, [First|_]),
    text(First, Suit).

text(c, club).
text(s, spade).
text(h, heart).
text(d, diamond).

update([], Suit, [[Suit,1]]).
update([[S,N]|Rest], Suit, Distribution) :-
    (   S = Suit
    ->  succ(N, N1),
        Distribution = [[S,N1]|Rest]
    ;   Distribution = [[S,N]|Rest1],
        update(Rest, Suit, Rest1) ).

示例:

?- distribution([sq,s9,s8,ha,hk,h5,da,dk,dj,d4,ca,c7,c6],D).
D = [[spade, 3], [heart, 3], [diamond, 4], [club, 3]].

备注 在 Prolog 中,将一对 [x,y] 表示为 x-y.

形式的项更为惯用

使用swi-prolog built-in谓词,另一种可能的解决方案是:

distribution2(Cards, Distribution) :-
    maplist(suit, Cards, Suits),
    msort(Suits, SortedSuits),
    clumped(SortedSuits, Distribution).

示例:

?- distribution2([sq,s9,s8,ha,hk,h5,da,dk,dj,d4,ca,c7,c6],D).
D = [club-3, diamond-4, heart-3, spade-3].

我想出了[编辑,使用来自 slago 的回答的 clumped 好多了]:

card_suite(Card, Suite) :-                                           % from s5
    atom_concat(Char, _, Card),                                      % to s + 5 = s5
    select(Char-Suite, [s-spade, h-heart, d-diamond, c-club], _).    % to spade

cards_distribution(Cards, Distribution) :-      % from [s5, c7, s2]
    sort(0, @=<, Cards, SortedCards),           % to [c7, s2, s5]
    maplist(card_suite, SortedCards, Suites),   % to [club, spade, spade]
    clumped(Suites, Distribution).              % to [club-1, spade-2]

例如

?- cards_distribution([sq,s9,s8,ha,hk,h5,da,dk,dj,d4,ca,c7,c6,s8],D).
D = [club-3, diamond-4, heart-3, spade-4]

相关logically pure group counting代码。

为什么不使用 复合词 表示卡片?

考虑红心皇后 ():hq 是一个原子,h(q) 是一个化合物。

这让事情变得简单多了:不需要排序并且一切都保持纯粹的关系

cards_([],[],[],[],[]).
cards_([Card|Cards],Cs,Ds,Hs,Ss) :-
   card_(Card,Cards,Cs,Ds,Hs,Ss).

card_(c(X),Cards,[X|Cs],Ds,Hs,Ss) :- cards_(Cards,Cs,Ds,Hs,Ss).
card_(d(X),Cards,Cs,[X|Ds],Hs,Ss) :- cards_(Cards,Cs,Ds,Hs,Ss).
card_(h(X),Cards,Cs,Ds,[X|Hs],Ss) :- cards_(Cards,Cs,Ds,Hs,Ss).
card_(s(X),Cards,Cs,Ds,Hs,[X|Ss]) :- cards_(Cards,Cs,Ds,Hs,Ss).

获取相应计数的示例查询:

?- Cards = [s(q),s(9),s(8),h(a),h(k),h(5),d(a),d(k),d(j),d(4),c(a),c(7),c(6)],
   cards_(Cards,Clubs,Diamonds,Hearts,Spades),
   length(Clubs,N_Clubs),
   length(Diamonds,N_Diamonds),
   length(Hearts,N_Hearts),
   length(Spades,N_Spades).
Cards      = [s(q),s(9),s(8),h(a),h(k),h(5),d(a),d(k),d(j),d(4),c(a),c(7),c(6)],
Clubs      = [a,7,6],
Diamonds   = [a,k,j,4],
Hearts     = [a,k,5],
Spades     = [q,9,8],
N_Clubs    = 3, 
N_Diamonds = 4,
N_Hearts   = 3, 
N_Spades   = 3.