我想计算列表中元素的出现次数

时间:2015-03-09 20:31:42

标签: list count prolog clpfd prolog-dif

我想计算列表中元素的出现次数,如果有,则谓词唯一将为true,否则为false。但是,如果元素出现不止一次,Prolog会发现它是真的。我不知道该怎么做......

count([], X, 0).
count([X|T], X, Y) :- count(T, X, Z), Y is 1+Z, write(Z).
count([_|T], X, Z) :- count(T, X, Z).

unique(St, [Y|RestList]) :- count([Y|RestList], St, N), N =:= 1.

2 个答案:

答案 0 :(得分:4)

只要第一个参数是地面列表,解决方案就可以工作。在其他一些情况下,它是不正确的:

?- count([E], a, 0).
false.

我们在这里问

  

长度为1的列表的元素E必须如何使列表包含0 a的出现?

事实上,这里有答案,例如E = bE = c

?- count([b],a,0).
true.

?- count([c],a,0).
true.

出于这个原因,Prolog的回答是不完整。应该说,是的。但是如何?

count([], _, 0).
count([E|Es], F, N0) :-
   count(Es, F, N1),
   if_(E = F, D = 1, D = 0),
   N0 is N1+D.

这使用if_/3(=)/3

?- length(Xs, I), count_dif(Xs, a, N).
   Xs = [],
   I = N, N = 0
;  Xs = [a],
   I = N, N = 1
;  Xs = [_A],
   I = 1,
   N = 0,
   dif(_A, a)
;  Xs = [a, a],
   I = N, N = 2
;  Xs = [_A, a],
   I = 2,
   N = 1,
   dif(_A, a) ;
   Xs = [a, _A],
   I = 2,
   N = 1,
   dif(_A, a) ;
   Xs = [_A, _B],
   I = 2,
   N = 0,
   dif(_A, a),
   dif(_B, a)
...

为了进一步改善这一点,我们可能会使用library(clpfd),因为它在SICStus,YAP和SWI中可用。

:- use_module(library(clpfd)).

count([], _, 0).
count([E|Es], F, N0) :-
   N0 #>= 0,
   if_(E = F, D = 1, D = 0),
   N0 #= N1+D,
   count(Es, F, N1).

现在甚至以下终止:

?- count([a,a|_], a, 1).
false.

?- N #< 2, count([a,a|_], a, N).
false.

答案 1 :(得分:1)

使用您自己的子句,我只是优化了一点程序:

  • 用匿名变量替换单例变量
  • 在计数谓词
  • 的第二个子句中添加剪切
  • 删除unique子句的第二个条件。

这是现在的程序:

count([],_,0).

count([X|T],X,Y):- !, count(T,X,Z), Y is 1+Z.

count([_|T],X,Z):- count(T,X,Z).

unique(St,L):- count(L,St,1).

协商:

?- count([2,3,4,3], 3,N).
N = 2.

?- unique(3, [2,3,4,5]).
true.