Prolog 收集项目并计算发生的次数

Prolog 收集项目并计算发生的次数,prolog,failure-slice,logical-purity,Prolog,Failure Slice,Logical Purity,我正在尝试编写一个递归规则collCount/2,它将列表中的相同项及其各自的出现次数分组到元组中 例如,collCount([a,b,a,b,c,b],F)将F与[(a,2)、(b,3)、(c,1)]绑定。运行此查询时,Prolog只返回no 以下是我迄今为止所做的工作: collCount([H|T],[(H,N)|L2]) :- countDel(H,[H|T],L,N), collCount(L,L2). countDel(X,T,Rest,N) :- occu

我正在尝试编写一个递归规则
collCount/2
,它将列表中的相同项及其各自的出现次数分组到元组中

例如,
collCount([a,b,a,b,c,b],F)
F
[(a,2)、(b,3)、(c,1)]
绑定。运行此查询时,Prolog只返回
no

以下是我迄今为止所做的工作:

collCount([H|T],[(H,N)|L2]) :-
    countDel(H,[H|T],L,N),
    collCount(L,L2).

countDel(X,T,Rest,N) :-
    occur(X,T,N),
    delAll(X,T,Rest).

occur(_,[],0).
occur(X,[X|T],N) :-
    occur(X,T,NN),
    N is NN + 1.
occur(X,[H|T],N) :-
    occur(X,T,N),
    X \= H.

delAll(_,[],[]).
delAll(X,[X|T],Ans) :-
    delAll(X,T,Ans).
delAll(X,[H|T],[H|Rest]) :-
    delAll(X,T,Rest),
    X \= H.
谓词
countDel/4
统计并删除列表中特定项的所有出现次数。例如,
countDel(2[1,2,3,2,2],L,N)
将L与
[1,3]
绑定,将
N与
3
绑定

谓词
出现/3
统计列表中特定项的所有出现次数。例如,
发生(3[1,2,3,4,3],Num)
Num
2
绑定

谓词
delAll/3
删除列表中特定项的所有出现。例如,
delAll(3[1,2,3,4,3],L)
L
[1,2,4]
绑定


非常感谢您的帮助。

您的代码基本正确。我已经把评论放在我修改它的地方了

collCount([],[]).  % miss base case
collCount([H|T],[(H,N)|L2]) :-
    countDel(H,[H|T],L,N),
    collCount(L,L2).

countDel(X,T,Rest,N) :-
    occur(X,T,N),
    delAll(X,T,Rest).

occur(_,[],0).
occur(X,[X|T],N) :-
    occur(X,T,NN),
    N is NN + 1.
occur(X,[H|T],N) :-
    occur(X,T,N),
    X \= H.

delAll(_,[],[]).
delAll(X,[X|T],Ans) :-
    delAll(X,T,Ans).
delAll(X,[H|T],[H|Rest]) :-
    X \= H,  % moved before recursive call
    delAll(X,T,Rest).
这就产生了

?- collCount([a,b,a,b,c,b],F).
F = [ (a, 2), (b, 3), (c, 1)] ;
false.
单向:

frequencies_of( []     , []     ) .  % empty list? success!
frequencies_of( [X|Xs] , [F|Fs] ) :- % non-empty list?
  count( Xs , X:1 , F , X1 ) ,       % count the occurrences of the head, returning the source list with all X removed
  frequencies_of( X1 , Fs )          % continue
  .                                  %

count( [] , F , F , [] ) .           % empty list? success: we've got a final count.
count( [H|T] , X:N , F , Fs ) :-     % otherwise...
  H = X ,                            % - if the head is what we're counting, 
  N1 is N+1 ,                        % - increment the count
  count( T , X:N1 , F , Fs )         % - recurse down
  .                                  %
count( [H|T] , X:N , F , [H|Fs] ) :- % otherwise...
  H \= X ,                           % - if the head is NOT what we're counting
  count( T , X:N , F , Fs )          % - recurse down, placing the head in the remainder list
  .                                  %
另一种看待它的方式:

frequencies_of( Xs , Fs ) :-     % compile a frequency table
  frequencies_of( Xs , [] , Fs ) % by invoking a worker predicate with its accumulator seeded with the empty list
  .

frequencies_of( []     , Fs , Fs ) .  % the worker predicate ends when the source list is exhausted
frequencies_of( [X|Xs] , Ts , Fs ) :- % otherwise...
  count( X , Ts , T1 ) ,              % - count X in the accumulator
  frequencies_of( Xs , T1 , Fs )      % - and recursively continue
  .                                   %

count( X , []       , [X:1]     ) .   % if we get to the end, we have a new X: count it
count( X , [X:N|Ts] , [X:N1|Ts] ) :-  % otherwise, if we found X,
  N1 is N+1                           % - increment the count
  .                                   % - and end.
count( X , [T:N|Ts] , [T:N|Fs]  ) :-  % otherwise
  X \= T ,                            % - assuming we didn't find X
  increment( X , Ts , Fs )            % - just continue looking
  .                                   % Easy!
第三种方法是先对列表排序,而不删除重复项。一旦对列表进行了排序,对排序列表进行简单的单程、游程编码,即可得到频率表,如下所示:

frequencies_of( Xs , Fs ) :- % to compute the frequencies of list elements
  msort( Xs , Ts ) ,         % - sort the list (without removing duplicates)
  rle( Ts , Fs )             % - then run-length encode the sorted list
  .                          % Easy!

rle( []    , [] ) .    % the run length encoding of an empty list is the empty list.
rle( [H|T] , Rs ) :-   % the run length encoding is of a non-empty list is found by
  rle( T , H:1 , Rs )  % invoking the worker on the tail with the accumulator seeded with the head
  .                    %

rle( []    , X:N , [X:N] ) .     % the end of the source list ends the current run (and the encoding).
rle( [H|T] , X:N , Rs    ) :-    % otherwise...
  H = X     ,                    % - if we have a continuation of the run,
  N1 is N+1 ,                    % - increment the count
  rle( T , X:N1 , Rs )           % - and recursively continue
  .                              %
rle( [H|T] , X:N , [X:N|Rs] ) :- % otherwise...
  H \= X ,                       % - if the run is at an end,
  rle( T , H:1 , Rs)             % - recursively continue, starting a new run and placing the current encoding in the result list.
  .                              %

我想提请您注意您和@Capelical解决方案的一小部分。无害的,如:

occur(_,[],0) :- false. occur(X,[X|T],N) :- occur(X,T,NN), false, N is NN + 1. occur(X,[H|T],N) :- occur(X,T,N), false, X \= H. 你看够了吗?当再添加一个元素时,推断的数量将加倍。简而言之,存在指数级开销!你需要把不平等放在第一位。更好的方法是使用dif(X,H)

请注意,此属性与是否为尾部递归无关。仍然有一些地方可以进行优化,但远不如这次


有关使用此技术的更多示例,请参阅。

有关逻辑纯实现,请参阅相关问题 “”

在这个答案中,我给出了一个
list_counts/2
的实现,它保留了

让我们使用
列表计数/2

?- list_counts([a,b,a,b,c,b],F).
F = [a-2, b-3, c-1].
请注意,
list_counts/2
表示
K
V
的成对,如
K-V
。一般来说,这比基于逗号
(K,V)
或列表
[K,V]
的表示更可取,原因有很多(可读性、与其他标准库谓词的互操作性、效率)

如果您确实需要使用基于逗号的表示法,您可以定义
collCount/2
,如下所示:

:- use_module(library(lambda)).

collCount(Xs,Fss) :-
    list_counts(Xs,Css),
    maplist(\ (K-V)^(K,V)^true,Css,Fss).
因此,让我们使用
collCount/2

?- collCount([a,b,a,b,c,b],F). F = [(a,2), (b,3), (c,1)]. % succeeds deterministically ?-collCount([a,b,a,b,c,b],F)。 F=[(a,2)、(b,3)、(c,1)]。%决定性地成功
编辑2015-05-13 出于好奇心,让我们考虑一下“虚”的性能方面。

下面的查询大致对应于@false在其答案中使用的查询。在这两方面,我们都感兴趣的是 通用终端:

?- length(As,M), M>9, maplist(=(a),As), time((list_item_subtracted_count0_count(As,a,_,1,_),false ; true)). % 73 inferences, 0.000 CPU in 0.000 seconds (95% CPU, 1528316 Lips) As = [a, a, a, a, a, a, a, a, a|...], M = 10 ; % 80 inferences, 0.000 CPU in 0.000 seconds (96% CPU, 1261133 Lips) As = [a, a, a, a, a, a, a, a, a|...], M = 11 ; % 87 inferences, 0.000 CPU in 0.000 seconds (96% CPU, 1315034 Lips) As = [a, a, a, a, a, a, a, a, a|...], M = 12 ... ?长度(As,M), M> 九,, 地图列表(=(a),As), 时间((列表项减去计数0计数(As,a,u,1,u),false;true))。 %73个推论,0.000秒内有0.000个CPU(95%的CPU,1528316个嘴唇) As=[a,a,a,a,a,a,a,a,a |…], M=10; %80个推论,0.000秒内使用0.000 CPU(96%CPU,1261133个嘴唇) As=[a,a,a,a,a,a,a,a,a |…], M=11; %87个推论,0.000秒内0.000 CPU(96%CPU,1315034个嘴唇) As=[a,a,a,a,a,a,a,a,a |…], M=12。。。
您是否考虑过先使用
msort/2
对列表进行排序?这将使工作更容易。除此之外,如果你能为你定义的每个谓语填写一个简短的描述(一句或两句),解释它们的意思,这将有助于澄清问题。是的,我有。然而,我试图避免使用内置谓词。我编写了一个递归规则对列表进行排序,但它只对包含数字的列表进行排序。此外,我还添加了上述谓词的描述。感谢您的回复。我可以问你为什么在递归调用之前移动这个目标吗?为了效率,我已经“自动”完成了。递归完整列表然后放弃结果是没有用的。。。但无论如何,这个计划应该会奏效。 ?- length(As,M), M>9, maplist(=(a),As), time((list_item_subtracted_count0_count(As,a,_,1,_),false ; true)). % 73 inferences, 0.000 CPU in 0.000 seconds (95% CPU, 1528316 Lips) As = [a, a, a, a, a, a, a, a, a|...], M = 10 ; % 80 inferences, 0.000 CPU in 0.000 seconds (96% CPU, 1261133 Lips) As = [a, a, a, a, a, a, a, a, a|...], M = 11 ; % 87 inferences, 0.000 CPU in 0.000 seconds (96% CPU, 1315034 Lips) As = [a, a, a, a, a, a, a, a, a|...], M = 12 ...