Search code examples
listcountprologclpfdprolog-dif

Counting occurrences in list


I'm trying to create a rule that counts the number of occurrences of a certain element in a given list, what I've tried so far does not seem to work the way I expect:

The first argument here should be the list, the second one the element we are looking for, and the last one the number of occurrences:

%empty list should always return 0 occurences
count([],E,0) :- true.

%if our head is what we are looking for, count
count([E|T],E,N) :- count(T,E,N-1).

%otherwise, do not count
count([H|T],E,N) :- H \== E, count(T,E,N).

Here H is the head and T the tail of the given list.

The base case e.g. count([],1,N). returns N = 0 as expected, but as soon as the list is non empty, we always get false.:

?- count([1],1,N).
false.
?- count([1,2,1,3],1,N).
false.

Can anyone point out what I'm doing wrong?

Update:

It seems to work when replacing the second line with

count([E|T],E,N+1) :- count(T,E,N).

But I just cannot figure out why this is not equivalent to my first idea.

Then we get

?- count([1,2,1,3],1,N).
N = 0+1+1 

which is correct.


Solution

  • The problem is that N+1 (or N-1) isn't evaluated, as can be seen by your second (working) example.

    % empty list has 0 occurrences
    count([], _, 0).
    
    % if our head is what we are looking for, count
    count([E|T], E, N) :-
        N_1 is N - 1,         % this is the important one
        count(T, E, N_1).
    
    % otherwise, do not count
    count([H|T], E, N) :-
        H \== E,
        count(T, E, N).
    

    is actually evaluates the equation, instead of unifying the N in your next call with N-1. That's why in your second example, you end up with N=0+1+1 instead of N=2.