Prolog - 计算列表中的重复次数

时间:2017-03-19 18:14:02

标签: list prolog

我正在Prolog中编写一个程序,用于计算列表中第一个值的不间断次数。

所以,repetitions(N, [a,a,a,a,a,b,c,a]),程序将返回N = 5

这是我的代码到目前为止的样子:

repetitions(A,[]).
repetitions(A,[A|T]) :- repetitions(A,[_|T]), A is 1+A.
repetitions(A,[_|T]) :- repetitions(A,[A|T]).

4 个答案:

答案 0 :(得分:4)

以下是关系版本:

repetitions(N, [First|Rest]) :-
        phrase(repetitions_(First, 1, N), Rest).

repetitions_(_, N, N) --> [].
repetitions_(First, N0, N) --> [First],
        { N1 #= N0 + 1 },
        repetitions_(First, N1, N).
repetitions_(First, N, N) --> [Other], { dif(First, Other) }, ... .

... --> [] | [_], ... .

测试用例按要求运行:

?- repetitions(N, [a,a,a,a,a,b,c,a]).
N = 5 ;
false.

此外,我们也可以在其他方向中使用它。

例如,一般包含3个元素的列表

?- Ls = [A,B,C], repetitions(N, Ls).
Ls = [C, C, C],
A = B, B = C,
N = 3 ;
Ls = [B, B, C],
A = B,
N = 2,
dif(B, C) ;
Ls = [A, B, C],
N = 1,
dif(A, B) ;
false.

所有可能的答案如何,通过迭代深化进行了相当的列举:

?- length(Ls, _), repetitions(N, Ls).
Ls = [_8248],
N = 1 ;
Ls = [_8248, _8248],
N = 2 ;
Ls = [_8734, _8740],
N = 1,
dif(_8734, _8740) ;
Ls = [_8248, _8248, _8248],
N = 3 ;
Ls = [_8740, _8740, _8752],
N = 2,
dif(_8740, _8752) ;
etc.

它是逻辑程序的一个主要吸引力,它们通常可用于多个方向

有关我用来实现此一般性的机制的详细信息,请参阅

我们也可以用它来回答以下问题

  

列表看起来像什么第一个元素有3次重复?

示例:

?- repetitions(3, Ls).
Ls = [_2040, _2040, _2040] ;
Ls = [_2514, _2514, _2514, _2532],
dif(_2514, _2532) ;
Ls = [_2526, _2526, _2526, _2544, _2550],
dif(_2526, _2544) ;
Ls = [_2538, _2538, _2538, _2556, _2562, _2568],
dif(_2538, _2556) .

这只需要在上面的解决方案中添加一个进一步的约束。我把这作为一个简单的练习。

答案 1 :(得分:4)

这是一个基于DCG的解决方案,有点像@mat's的变种:

repetitions_II(N, [X|Cs]) :-
   phrase( ( reps(X, N), no(X) ), [X|Cs]).

no(X) -->
   ( [] | [Y], {dif(X,Y)}, ... ).

reps(_X, 0) -->
   [].
reps(X, N0) -->
   [X],
   { N0 #> 0, N1 #= N0-1 },
   reps(X, N1).

两个值得注意的差异:

1mo)维持计数器没有任何区别。因此,对数量的约束可以帮助改善终止。一个完美的clpfd实现将(或者更应该)以相似的效率实现这一点。

2do)结尾no//1基本上以纯粹的方式编码\+[X]

这个解决方案的缺点是它仍然会产生剩余的选择点。要摆脱这些,需要一些更多的手动编码:

:- use_module(library(reif)).

repetitions_III(N, [X|Xs]) :-
   reps([X|Xs], X, N).

reps([], _, 0).
reps([X|Xs], C, N0) :-
   N0 #>= 0,
   if_(X = C, ( N1 #= N0-1, reps(Xs, C, N1) ), N0 = 0 ).

答案 2 :(得分:2)

使用CLPFD的另一种接近你目前所做的方法:

:- use_module(library(clpfd)).

repetitions(N,[H|T]):-repetitions(N,[H|T],H).

repetitions(0,[],_).
repetitions(0,[H|_],H1):-dif(H,H1).
repetitions(N,[H|T],H):-repetitions(N1 ,T, H), N #= N1+1.

示例:

?- repetitions(A,[a,a,a,a,a,b,c,a]).
A = 5 ;
false.

?- repetitions(2,[a,Y]).
Y = a.

?- repetitions(N,[a,a|_]).
N = 2 ;
N = 2 ;
N = 3 ;
N = 3 ;
N = 4 ;
N = 4 ;
N = 5 ;
N = 5 ;
N = 6 ;
N = 6 ....and goes on

答案 3 :(得分:1)

一个紧凑的定义,礼貌图书馆applyyall

?- [user].
repetitions(A,[H|T]) :- foldl([E,(C0,H0),(C1,H1)]>>(E==H0 -> succ(C0,C1), H1=H0 ; C0=C1, H1=_), T, (1,H), (A,_)).
|: true.

?- repetitions(A,[a,a,a,a,a,b,c,a]).
A = 5.