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

时间:2013-12-10 01:12:00

标签: list prolog

我正在尝试查看列表并计算给定单词出现的次数。到目前为止我已经有了这个:

count_repetitions([_], [], 0).
count_repetitions([Word], [Word|Tail], Count):-
   count_repetitions([Word], Tail, X), 
   Count is X + 1.
count_repetitions([Word], [Z|Tail], Count):-
   Word \= Z, 
   count_repetitions([Word], Tail, Count).

因此,查询?- count_repetitions([yes],[yes,and,yes,and,no], X).会提供X = 2

这似乎有效。现在我需要编写一个谓词,它以X = [(yes - 2)]的形式输出一个包含搜索词及其出现次数的列表。我完全陷入困境,有什么建议吗?

3 个答案:

答案 0 :(得分:7)

这个答案显示了逻辑上纯粹的方式。以下内容基于

tcount/3

我们与类似地定义tfilter/3 :- meta_predicate tcount(2,?,?). tcount(P_2,Xs,N) :- N #>= 0, list_pred_tcount_(Xs,P_2,0,N). :- meta_predicate list_pred_tcount_(?,2,?,?). list_pred_tcount_([] , _ ,N ,N). list_pred_tcount_([X|Xs],P_2,N0,N) :- if_(call(P_2,X), (N1 is N0+1, N1 #=< N), N1 = N0), list_pred_tcount_(Xs,P_2,N1,N).

tcount/3

现在让我们将?- tcount(=(yes),[yes,and,yes,and,no],Count). Count = 2. (=)/3结合使用:

?- tcount(=(yes),[A,B,C,D],2).
      A=yes ,     B=yes , dif(C,yes), dif(D,yes)
;     A=yes , dif(B,yes),     C=yes , dif(D,yes)
;     A=yes , dif(B,yes), dif(C,yes),     D=yes
; dif(A,yes),     B=yes ,     C=yes , dif(D,yes)
; dif(A,yes),     B=yes , dif(C,yes),     D=yes
; dif(A,yes), dif(B,yes),     C=yes ,     D=yes
; false.

所有此问题的其他答案所提供的代码不同,此答案中提供的代码是单调,即使在非基础术语中使用它时仍然保持逻辑合理:

?- tcount(=(yes),[A,B,C,D],Count).
      A=yes ,     B=yes ,     C=yes ,     D=yes , Count = 4
;     A=yes ,     B=yes ,     C=yes , dif(D,yes), Count = 3
;     A=yes ,     B=yes , dif(C,yes),     D=yes , Count = 3
;     A=yes ,     B=yes , dif(C,yes), dif(D,yes), Count = 2
;     A=yes , dif(B,yes),     C=yes ,     D=yes , Count = 3
;     A=yes , dif(B,yes),     C=yes , dif(D,yes), Count = 2
;     A=yes , dif(B,yes), dif(C,yes),     D=yes , Count = 2
;     A=yes , dif(B,yes), dif(C,yes), dif(D,yes), Count = 1
; dif(A,yes),     B=yes ,     C=yes ,     D=yes , Count = 3
; dif(A,yes),     B=yes ,     C=yes , dif(D,yes), Count = 2
; dif(A,yes),     B=yes , dif(C,yes),     D=yes , Count = 2
; dif(A,yes),     B=yes , dif(C,yes), dif(D,yes), Count = 1
; dif(A,yes), dif(B,yes),     C=yes ,     D=yes , Count = 2
; dif(A,yes), dif(B,yes),     C=yes , dif(D,yes), Count = 1
; dif(A,yes), dif(B,yes), dif(C,yes),     D=yes , Count = 1
; dif(A,yes), dif(B,yes), dif(C,yes), dif(D,yes), Count = 0.

让我们尝试一些更为通用的东西:

?- tcount(_,_,-1).
false.

下面的角落情况怎么样?

tcount/3

如何利用length/2替代{{1}}?

?- N in 1..3, length(Xs,N).
  N = 1, Xs = [_A]
; N = 2, Xs = [_A,_B]
; N = 3, Xs = [_A,_B,_C]
...                                      % does not terminate

?- use_module(library(lambda)).
true.

?- N in 1..3, tcount(\_^ =(true),Xs,N).
  N = 1, Xs = [_A]
; N = 2, Xs = [_A,_B]
; N = 3, Xs = [_A,_B,_C]
; false.                                 % terminates universally

答案 1 :(得分:4)

你在那里,在我看来。你可以简单地将你的谓词包装在另一个中:

word_repetitions(Word, List, [(Word-Count)]) :-
    count_repetitions(Word, List, Count).

请注意,您不需要括号或Word-Count对周围的括号:

word_repetitions(Word, List, Word-Count) :-
    count_repetitions(Word, List, Count).

(但如果你坚持的话,你可以使用它们。)

在原始谓词上,重命名以反映差异:

list_word_reps([], Word, Word-0).
list_word_reps([W|Rest], Word, Word-Reps) :-
    list_word_reps(Rest, Word, Word-Reps0),
    (   W == Word
    ->  Reps is Reps0 + 1
    ;   Reps = Reps0
    ).

?- list_word_reps([yes,no,yes,no,maybe,yes], yes, X).
X = yes-3.

列表出现在单词之前的原因是谓词变为确定性。使用if-then-else代替两个不同的子句也是如此。如果你愿意,你可以把答案放在一个列表中(只需将参数括在括号中)但是再次,这是不必要的。

答案 2 :(得分:2)

库(聚合)经常被低估:

count(L, C) :-
    aggregate(set(W-N), aggregate(count, member(W, L), N), C).

产量

1 ?- count([a,b,a],C).
C = [a-2, b-1].

所以,更简单

count(W, L, W-N) :-
    aggregate(count, member(W, L), N).

产量

?- count(a, [a,b,a], C).
C = a-2.

基于setof,aggregate / 3允许更精细地控制变量的量化(即哪些值得到聚合),但如果没有解决方案则会失败,而不是因为某些时候需要产生0。

aggregate_all / 3,基于findall / 3,在这种情况下会返回0,但不允许使用量化说明符。