在Python中你可以做到
>>> import from collections counter
>>> Counter(['a','b','b','c'])
>>> Counter({'b': 2, 'a': 1, 'c': 1})
Prolog中有类似的东西吗?像这样:
counter([a,b,b,c],S).
S=[a/1,b/2,c/1].
这是我的实施:
counter([],List,Counts,Counts).
counter([H|T],List,Counts0,[H/N|Counts]):-
findall(H, member(H,List), S),
length(S,N),
counter(T,List,Counts0,Counts).
counter(List,Counts):-
list_to_set(List,Set),
counter(Set,List,[],Counts).
它相当冗长,所以我想知道是否有内置谓词或更简洁的实现。
答案 0 :(得分:5)
没有内置谓词,这是另一种方法:
counter([X], [X/1]).
counter([H | T], R) :-
counter(T, R1),
( select(H/V, R1, R2)
-> V1 is V+1,
R = [H/V1 | R2]
; R = [H/1 | R1]).
答案 1 :(得分:1)
我喜欢@ joel76&#39的解决方案。我将在主题上添加更多变体。
VARIATION I
这是另一个简单的方法,首先对列表进行排序:
counter(L, C) :-
msort(L, S), % Use 'msort' instead of 'sort' to preserve dups
counter(S, 1, C).
counter([X], A, [X-A]).
counter([X,X|T], A, C) :-
A1 is A + 1,
counter([X|T], A1, C).
counter([X,Y|T], A, [X-A|C]) :-
X \= Y,
counter([Y|T], 1, C).
快速试用:
| ?- counter([a,b,b,c], S).
S = [a-1,b-2,c-1] ?
yes
这将在counter([], C).
上失败,但如果您希望它成功,您可以简单地包含条款counter([], []).
。它没有保持元素出现的初始顺序(它不清楚这是否是一个要求)。这个实现非常有效并且是尾递归的,只要第一个参数被实例化它就会起作用。
VARIATION II
此版本将维护元素的外观顺序,并在counter([], []).
上成功。它也是尾递归的:
counter(L, C) :-
length(L, N),
counter(L, N, C).
counter([H|T], L, [H-C|CT]) :-
delete(T, H, T1), % Remove all the H's
length(T1, L1), % Length of list without the H's
C is L - L1, % Count is the difference in lengths
counter(T1, L1, CT). % Recursively do the sublist
counter([], _, []).
有一些结果:
| ?- counter([a,b,a,a,b,c], L).
L = [a-3,b-2,c-1]
yes
| ?- counter([], L).
L = []
yes
VARIATION III
这个使用一个不是尾递归的帮助器,但它保留了元素的原始顺序,相当简洁,我认为更有效。
counter([X|T], [X-C|CT]) :-
remove_and_count(X, [X|T], C, L), % Remove and count X from the list
counter(L, CT). % Count remaining elements
counter([], []).
% Remove all (C) instances of X from L leaving R
remove_and_count(X, L, C, R) :-
select(X, L, L1), !, % Cut to prevent backtrack to other clause
remove_and_count(X, L1, C1, R),
C is C1 + 1.
remove_and_count(_, L, 0, L).
只要实例化counter
的第一个参数,此实现就会起作用。
在上面的谓词中,我使用Element-Count
模式而不是Element/Count
,因为一些Prolog解释器,特别是SWI,提供了许多谓词,这些谓词知道如何操作{{1的关联列表}对(参见SWI library(pairs)和ISO谓词keysort/2)。
答案 2 :(得分:1)
我也喜欢@ joel76解决方案(和@mbratch建议,也)。在这里,我只是要注意库(aggregate)(如果可用)具有计数聚合操作,可以与ISO内置setof / 3一起使用:
counter(L, Cs) :-
setof(K-N, (member(K, L), aggregate(count, member(K, L), N)), Cs).
产量
?- counter([a,b,b,c], L).
L = [a-1, b-2, c-1].
如果选择操作更复杂,那么避免文本重复代码的好方法可能是
counter(L, Cs) :-
P = member(K, L),
setof(K-N, (P, aggregate(count, P, N)), Cs).
修改的
由于我假设库(聚合)可用,因此也可以更好地为它设置任务:
counter(L, Cs) :-
P = member(E,L), aggregate(set(E-C), (P, aggregate(count,P,C)), Cs).