Prolog: contare le ripetizioni nell’elenco

Sto cercando di guardare attraverso una lista e contare il numero di volte che appare una determinata parola. Ho ottenuto questo finora:

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). 

Quindi la domanda ?- count_repetitions([yes],[yes,and,yes,and,no], X). darebbe X = 2 .

Questo sembra funzionare. Ora ho bisogno di scrivere un predicato che generi una lista con la parola di ricerca e il numero di volte in cui appare, nel formato X = [(yes - 2)] . Sono completamente bloccato, qualche suggerimento?

Ci sei, già, mi sembra. Potresti semplicemente avvolgere il tuo predicato in un altro dicendo:

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

Si noti che non è necessaria la parentesi o le parentesi attorno alla coppia Word-Count :

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

(ma puoi usarli se insisti).

Sul tuo predicato originale, rinominato per riflettere le differenze:

 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. 

Il motivo per cui la lista viene prima della parola è che il predicato diventa deterministico. Lo stesso vale per usare l’if-then-else invece di due diverse clausole. Puoi mettere la risposta in una lista se vuoi (basta avvolgere l’argomento tra parentesi) ma, ancora una volta, non è necessario.

Questa risposta mostra un modo logicamente puro per farlo. Il seguente è basato su clpfd .

 :- use_module(library(clpfd)). 

Definiamo il meta-predicato tcount/3 modo simile a 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). 

Ora usiamo tcount/3 in combinazione con (=)/3 :

 ?- tcount(=(yes),[yes,and,yes,and,no],Count). Count = 2. 

A differenza del codice presentato da tutte le altre risposte a questa domanda, il codice presentato in questa risposta è monotono e rimane logicamente valido anche quando lo si utilizza con termini non di terra:

 ?- 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. 

Proviamo qualcosa di ancora più generale:

 ?- 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. 

E il seguente caso d'angolo?

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

E che ne dici di utilizzare tcount/3 come alternativa alla length/2 ?

 ? - N in 1..3, lunghezza (Xs, N).
   N = 1, Xs = [_A]
 ;  N = 2, Xs = [_A, _B]
 ;  N = 3, Xs = [_A, _B, _C]
 ...% non termina

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

 ? - N in 1..3, tcount (\ _ ^ = (vero), Xs, N).
   N = 1, Xs = [_A]
 ;  N = 2, Xs = [_A, _B]
 ;  N = 3, Xs = [_A, _B, _C]
 ;  falsa.  % termina universalmente

la biblioteca (aggregato) è spesso sottovalutata:

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

i rendimenti

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

quindi, il più semplice

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

i rendimenti

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

essendo basato su setof, aggregato / 3 consente un controllo più preciso sulla quantificazione delle variabili (cioè quali valori vengono aggregati), ma fallirà se non c’è una soluzione, invece di produrre 0, come talvolta è necessario.

aggregate_all / 3, basato su findall / 3, restituisce 0 in questi casi, ma non consente gli specificatori di quantificazione.