Prolog中的Python计数器

时间:2014-03-22 16:35:58

标签: prolog

在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).

它相当冗长,所以我想知道是否有内置谓词或更简洁的实现。

3 个答案:

答案 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的第一个参数,此实现就会起作用。

<小时/> SIDEBAR

在上面的谓词中,我使用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).