我想要计算列表中一个元素出现的次数。

3

我想要在列表中计算某个元素的出现次数,如果只出现一次,则谓词“unique”为真,否则为假。然而,如果该元素出现多次,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个回答

4

该解决方案只适用于第一个参数是一个确定列表的情况。在其他一些情况下,它是不正确的:

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

这里我们要问的是:

如果一个列表的长度为1且其中不包含任何“a”元素,那么这个列表中的元素E应该长成什么样子?

实际上有许多可能的答案,比如E = b 或者 E = 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

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

  • 将单例变量替换为匿名变量
  • 在计数谓词的第二个子句上添加一个割
  • 删除唯一子句的第二个条件。

这是现在的程序:

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.

哇,它完美地工作了,你是最棒的,非常感谢。我已经花费了很多很多小时在这个练习上,非常感谢!!! - Xanthoula Atsalaki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接