不完美的Prolog代码用于查找具有某些条件的元素

时间:2014-04-10 15:51:26

标签: list prolog

第一段代码:

count(X, [], 0).
count(X, [X|T], N) :-
  count(X, T, N1),
  N is N1 + 1.
count(X, [Y|T], N) :-
  X \= Y,
  count(X, T, N).

它显示列表中给定元素的重复次数。 (没有问题!)例如,

?- count(5, [1,5,5,3,3,3,4,2], X).
X = 2.

第二段代码:

found(0, [], X).
found(N, [H|T], R):-
  count(R, [R|T], C),
  C == N.
found(N, [H|T], R):-
  count(R, [R|T], C),
  C \= N,
found(N, T, R).

通过给出列表中的重复次数来显示元素。 (但它并不完美)。例如,

?- found(2, [1,5,5,3,3,3,4], X).
X = 5. 

这没关系。然而,

?- found(3, [1,5,5,3,3,3,4], X).
X = 5.

这是错的!我不知道如何解决它。

2 个答案:

答案 0 :(得分:1)

首先要做的事情! count/3found/3 相同

我们使用 tcount/3和明确的术语平等(=)/3来定义count/3

:- use_module(library(clpfd)).

count(E,Xs,N) :-
   tcount(=(E),Xs,N).

问:“Ne的出现次数[a,e,e,c,c,c,d,b]是多少?”

?- count(e,[a,e,e,c,c,c,d,b],N).
N = 2.

问:“X中哪个[a,e,e,c,c,c,d,b] >发生三次以上?”

?- N #> 3, count(X,[a,e,e,c,c,c,d,b],N).
false.

问:“X中哪个[a,e,e,c,c,c,d,b]正好发生三次?”

?- count(X,[a,e,e,c,c,c,d,b],3). 
  X = c
; false.

问:“X中哪个[a,e,e,c,c,c,d,b]发生正好两次?”

?- count(X,[a,e,e,c,c,c,d,b],2).
  X = e
; false.

问:“X中哪个[a,e,e,c,c,c,d,b]只发生一次?”

?- count(X,[a,e,e,c,c,c,d,b],1).
  X = a
; X = d
; X = b
; false.

问:“X中哪个[a,e,e,c,c,c,d,b] 完全为零

?- count(X,[a,e,e,c,c,c,d,b],0).
dif(X,a), dif(X,b), dif(X,c), dif(X,d), dif(X,e).

答案 1 :(得分:0)

你说count/3没有问题,但我不同意:

?- count(X, [1,5,5,3,3,3,4,2], Z).
X = Z, Z = 1 ;
false.

我想在这里看到的是:

X = Z, Z = 1 ;
X = 5, Z = 2 ;
X = Z, Z = 3 ;
X = 4, Z = 1 ;
X = 2, Z = 1 ;
false.

在我看来count/3应该能够列出第一个以外的列表项。事实上,它只能提出,因为第二条规则对列表的头部有所说明。由于第一条规则,您的单身警告会出现,这可以更好地说明:

count(_, [], 0).

您对该规则的真实含义是,"无论您要查找什么,如果列表为空,则该项的计数为0。"我可以想出其他方法来重述为第一个参数生成的谓词,但是没有一个方法可以保留这个非常令人愉悦的属性。我怀疑@false会在某个时刻出现并告诉我们两个为什么我们都错了。但这就是我倾向于陈述count/3的方式:

count(Item, List, 0) :- \+ memberchk(Item, List).
count(Item, List, N) :-
    % get us each unique item, one by one (or confirm that X is in the list)
    sort(List, Sorted), member(Item, Sorted),

    % make a list of the items that match
    bagof(Item, member(Item, List), Occurrences),

    % count them
    length(Occurrences, N).

这很奇怪而且很尴尬,可能有更好的方法,但它符合我们的要求:

?- count(X, [1,5,5,3,3,3,4,2], Z).
X = Z, Z = 1 ;
X = 2,
Z = 1 ;
X = Z, Z = 3 ;
X = 4,
Z = 1 ;
X = 5,
Z = 2.

?- count(0, [1,5,5,3,3,3,4,2], Z).
Z = 0 ;
false.

?- count(5, [1,5,5,3,3,3,4,2], Z).
Z = 2.

现在让我们把注意力转向found/3。首先让我们更改代码以解决单例错误,以便我们可以看到问题所在:

found(0, [], _).
found(N, [_|T], R):-
  count(R, [R|T], C),
  C == N.
found(N, [_|T], R):-
  count(R, [R|T], C),
  C \= N,
  found(N, T, R).

我们立即看到,在第二和第三条规则中,我们在列表的前面暗示了一个R.可能在这两种情况下我们都希望保留H并传递它或做一些棘手的事情。事实上,使用L可能更安全,因为我们不应该对列表做任何奇特的事情 - count/3过程应该足够了。在检查时,我不知道你试图用第三条规则完成什么。事实上,我们可以完全删除它,我们得出以下定义:

found(0, [], _).
found(N, L, R):-
  count(R, L, C),
  C == N.

它适用于您的两个用例,但猜猜是什么?我们根本不需要found/3,因为count/3现在可以做到了!

?- count(X, [1,5,5,3,3,3,4], 3).
X = 3 ;
false.

?- count(X, [1,5,5,3,3,3,4], 2).
X = 5.

这些实际上是在问问题" 2次出现的元素是什么?" Prolog消除了对整个其他谓词的需求。您现在可以通过以下简单的定义轻松确定它:

found(X, Y, Z) :- count(Z, Y, X).

一个分手评论:如果您使用真实的非单字母变量名称,您可能会发现您的代码更容易调试。看看我对count/3的定义。它有点大,但更容易理解。我经常在自己的代码中使用[H|T][X|Xs],但一般来说,X,Y,Z等的扩散使得在阅读代码时很难遵循,并且阅读代码是在声明性语言中非常重要,其中大部分流程实际上是关于在哪里使用哪些变量。