2012-02-22 5 views
4

Telle est la question pour un de mes missions:Prolog Affectation

Ecrire repCount(L, X, N) qui est vrai quand N est le nombre d'occurrences de X dans la liste L.

Voici mon code où je tente d'aborder le problème récursive:

repCount([], X, N) :- 
    N is 0. 
repCount([H|T], X, N) :- 
    count([H|T], X, N). 

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

Et ça marche quand je fournir une liste complète des numéros identiques à ceci:

?- repCount([2,2,2], 2, N). 
N = 3. 

Mais si je fournir une liste avec au moins une valeur différente:

?- repCount([2,2,22], 2, N). 
false. 

Renvoie false. Je ne peux pas comprendre pourquoi cela se produit ou comment le changer pour «ignorer» la valeur qui ne correspond pas, plutôt que de déclarer tout faux. Toute contribution est appréciée.

Répondre

4
count([H|T], X, N):- count(T, X, N1), X=:=H, N is N1 + 1. 

Ici, vous déclarez que N doit être N1 + 1 si X est H; Cependant, vous ne définissez pas ce qui devrait se produire si X ne soit pas H (essentiellement manque une clause else) cela devrait fonctionner:

count([H|T], X, N):- 
    count(T, X, N1), 
    (X=:=H-> 
     N is N1 + 1 
     ; N is N1). 

une autre façon serait:

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

count([H|T], X, N):- X=\=H, count(T, X, N1), N is N1. 

mais cela est inefficace puisque le nombre (T, X, N1) sera appelé deux fois si X n'est pas H. nous pouvons résoudre ce problème en faisant le chèque à la tête de la clause:

count([H|T], H, N):- count(T, X, N1), N is N1 + 1. 

count([H|T], X, N):- count(T, X, N1), N is N1. 

ou simplement: count ([H | T], H, N): - le nombre (T, X, N1), N est N1 + 1.

count([H|T], X, N1):- X=\=H, count(T, X, N1). 
+0

beaucoup apprécié, je vous remercie. –

+0

Vous devriez cliquer sur upvote au lieu de dire "merci" sans upvoting le gars qui vous a aidé! –

+1

Trop court de rep pour le moment, mais fera une fois que j'en ai assez. Merci pour la note! –

0

Presque là ... vous devez utiliser un accumulateur , donc:

repCount(Xs,Y,N) :- 
    count(Xs,Y,0,N) % the 3rd argument is the accumulator for the count, which we seed as 0 
    . 

count([],_,N,N).  % if the list is empty, unify the accumulator with the result 
count([X|Xs],Y,T,N) :- % if the list is non-empty, 
    X == Y ,    % and the head of the list (X) is the the desired value (Y), 
    T1 is T+1 ,   % then increment the count, and 
    count(Xs,Y,T1,N)  % recurse down, passing the incremented accumulator 
    .     % 
count([X|Xs],Y,T,N) :- % if the list is non-empty, 
    X \== Y ,   % and the head of the list(X) is not the desired value (Y), 
    count(Xs,Y,T,N)  % simply recurse down 
    .     % 
0

La question initiale n'a pas précisé s'il existait des contraintes sur les prédicats que vous pourriez utiliser.

Si vous êtes autorisé à «tricher» ie. utiliser des prédicats d'ordre supérieur comme « findall » qui récursion pour vous Vs vous faire la récursion vous, cela peut être fait dans un seul prédicat:

repCount(L, X, N) :- 
    findall(X, member(X, L), ListOfX), 
    length(ListOfX, N). 
1

Mais en supposant que vous n'êtes pas autorisé à « tricher », si vous voulez d'utiliser récursion, vous n'avez pas besoin de faire la comparaison « == » .. vous pouvez utiliser l'unification variable Prolog pour atteindre le même but:

% Job done all instances 
repCount2([], _, 0). 

% Head unifies with X/2nd parameter - ie X found 
repCount2([H|T], H, N) :- 
    repCount2(T, H, NewN), 
    N is NewN + 1. 

% We got here, so X not found, recurse around 
repCount2([_|T], X, N) :- 
    repCount2(T, X, N). 

Dans le second prédicat, H est mentionné deux fois, ce qui signifie que Si le chef de la liste est le même que X, alors recurse vers le bas, puis ajouter 1 au résultat du reste de la récursivité (qui se termine par l'ajout de 0 - le cas de base, qui est la façon dont le cumul ator est construit).

+0

Vrai, mais ... Si vous allez le faire, vous avez besoin d'un cut ('!') Dans la 2ème clause pour éliminer le point de choix sur le backtracking, de peur que vous ne trouviez plusieurs résultats. De plus, si le terme de recherche n'est pas lié ou si la liste contient des éléments non liés, l'unification est susceptible de produire ... des résultats inattendus. –

+0

Bonne réponse Nicholas - merci. – magus

+1

@magus: Il vaut mieux utiliser dif/2 dans la dernière clause sans '!/0'. Un '!/0' rend votre programme inutilement impur. – false

2

Un ajout intéressant peut-être à ce que @magus a écrit: Si vous ne se soucient que le nombre d'éléments au lieu des éléments eux-mêmes, vous pouvez utiliser findall/3 comme ceci:

list_elem_num(Ls, E, N) :- 
    findall(., member(E, Ls), Ds), 
    length(Ds, N). 
2

Preserve -Sur peu d'aide de tcount/3 et (=)/3!

Le but tcount(=(X),Es,N) indique "il y a N éléments dans la liste Es qui sont égaux à X".

Exemple de requête:

 
?- tcount(=(X), [a,b,c,a,b,c,a,b,a], N). 
( N = 4,  X=a 
; N = 3,    X=b 
; N = 2,       X=c 
; N = 0, dif(X,a), dif(X,b), dif(X,c) 
).          % terminates universally