Prolog count frequencies

时间:2018-08-22 13:46:26

标签: prolog

Is it possible to write a predicate which takes an input list and "outputs" (succeeds) an output list with key-value pairs

example:

freqs([a,b,a,a,b,c],L).
L = [(a,3),(b,2),(c,1)]

I'd prefer to do this in O(n) if possible. The furthest I've gotten is this

freqs([],[]).
freqs(In,Out):-
    freqs(In,[],Out).

freqs([],Out,Out).
freqs([X|Xs],Table,Out):-
    \+ member((X,_),Table),
    freqs(Xs,[(X,1)|Table],Out).

freqs([X|Xs],Table,Out) :-
    member((X,N),Table),
    % stuck

more specifick, how to increment N? And is there an other solution possible which doesn't need an auxiliary predicate?

2 个答案:

答案 0 :(得分:3)

您可以使用公共库谓词select/3(或selectchk/3(如果也可用))代替member/2。类似于(对于第三个子句):

freqs([X|Xs],Table,Out) :-
    selectchk((X,N),Table, Others),
    M is N + 1,
    freqs(Xs, [(X,M)| Others], Out).

但是,由于您似乎担心性能,因此如果将第二和第三子句组合在一起,则会更快,从而得到以下完整的谓词定义:

freqs([], Out, Out).
freqs([X| Xs], Table, Out) :-
    (   select((X,N), Table, Others) ->
        M is N + 1,
        freqs(Xs, [(X,M)| Others], Out)
    ;   freqs(Xs, [(X,1)| Table], Out)
    ).

这样,您只需在每个输入列表元素中查找一次(X,N)项在表中的出现。

通话示例:

?- freqs([a,b,a,a,b,c],L).
L = [(c, 1),  (b, 2),  (a, 3)].

另一种解决方案是先使用标准sort/2谓词对输入列表进行排序(通常为O(n log(n))),然后对结果排序后的列表进行一次遍历,当然, 上)。因此O(n * log(n))+ O(n)复杂度。但是,正如Will Ness解释的那样,如果您的输入列表很大,则可能值得在Prolog系统库中寻找良好的 dictionary 实现。

答案 1 :(得分:1)

以状态传递方式编写谓词 function ,在遍历列表时更新表,就像您在函数式编程语言中所做的那样,请更改副本而不是(不可能的)副本值的突变。

对于线性表,当然是 O(n 2

将其维护为开放式二进制搜索树(叶上具有未实例化的logvar,以便在遇到新关键字时对其进行扩展)会将复杂度降低至 O(n log n),和往常一样。为此,您的密钥必须具有可比性。原子是。

有关可扩展查找表的示例,请参见attr/2(仅在那里是列表;将它做成树也是完全可行的。)