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?
答案 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
(仅在那里是列表;将它做成树也是完全可行的。)