我正在尝试编写一个递归规则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
绑定。
谓词occur/3
计算列表中特定项的所有出现次数。例如,occur(3,[1,2,3,4,3],Num)
将Num
与2
绑定。
谓词delAll/3
删除列表中特定项目的所有匹配项。例如,delAll(3,[1,2,3,4,3],L)
将L
与[1,2,4]
绑定。
非常感谢任何帮助。
答案 0 :(得分:3)
我想提请你注意你和@ CapelliC解决方案的一小部分内容。无害的:
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.
所以我在这里做的是在你的程序中加入一些 false
目标。通过这种方式,这个程序现在将采取比没有更少的推论。不过,有一些值得注意的事情。考虑:
?- length(As,M), M>9, maplist(=(a),As), \+ time(occur(a,As,_)). % 3,072 inferences, 0.002 CPU in 0.002 seconds (100% CPU, 1989931 Lips) As = [a, a, a, a, a, a, a, a, a|...], M = 10 ; % 6,144 inferences, 0.003 CPU in 0.003 seconds (100% CPU, 2050613 Lips) As = [a, a, a, a, a, a, a, a, a|...], M = 11 ; % 12,288 inferences, 0.006 CPU in 0.006 seconds (100% CPU, 2128433 Lips) As = [a, a, a, a, a, a, a, a, a|...], M = 12
你看够了吗?添加另一个元素时,推理数量会翻倍。简而言之,指数开销!你需要首先考虑不平等的目标。更好的是,使用dif(X, H)
。
请注意,此属性与尾递归无关。仍然有一些优化的地方,但远不如这个。
有关使用此技术的更多示例,请参阅failure-slice。
答案 1 :(得分:3)
对于逻辑上纯粹的实现,请查看相关问题的my answer " How to count number of element occurrences in a list in Prolog"
在该答案中,我提出了一个list_counts/2
的实现,它保留了logical-purity。
让我们使用list_counts/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
出于好奇,让我们考虑his answer中@false所提到的表现方面。
以下查询大致对应于@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 ...
答案 2 :(得分:0)
你的代码大多是正确的。我在修改过的地方放置了评论。
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.
答案 3 :(得分:0)
一种方式:
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!
第三种方法是首先对列表进行排序,而不删除重复项。订购列表后,有序列表的简单1遍行程编码为您提供频率表,如下所示:
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.
. %