1

I'm learning prolog, and I want to count a specific element occurence in a list.

So here is the code -

count(_, [], _) := !.

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

count(El, [_|T], N) :-
    count(El, T, N).

check(List1, List2) :-
    count(3, List1, M),
    count(2, List2, N),
    M is N.

so basically I would like to pass to console check([3,4,3], [2,3,4,5,2]), and it should return true, because occurences of 3 in list1 is the same as occurences of 2 in list2. But instead it throws me -

Arguments are not sufficiently instantiated. 
Exception: (10) _G1383 is _L155+1 ? creep
Exception: (9) count(3, [3, 4, 2], _G1385) ? creep
Exception: (8) count(3, [2, 3, 4, 2], _G1385) ? creep
Exception: (7) check([2, 3, 4, 2], [2, 3, 4]) ? creep 

What is the cause of this, and how can I solve it? I checked all around forum, and everywhere it's written that this should work. Is this some kind of version related stuff, or really I'm missing here something?

EDIT: Using SWI-Prolog.

EDIT2:

Got it working, thank you!

Code:

count(_, [], 0) :- !.

count(El, [El|T], N) :-
    count(El, T, N1),
    N #= N1 + 1.

count(El, [Y|T], N) :-
    El \= Y,
    count(El, T, N).

check(List1, List2) :-
    count(3, List1, M),
    count(2, List2, N),
    M #= N.
user980952
  • 35
  • 9

2 Answers2

2

It's great that you got it working now, by using declarative arithmetic!

I have a few minor additional comments about the solution you obtained, i.e.:

count(_, [], 0) :- !.

count(El, [El|T], N) :-
    count(El, T, N1),
    N #= N1 + 1.

count(El, [Y|T], N) :-
    El \= Y,
    count(El, T, N).

check(List1, List2) :-
    count(3, List1, M),
    count(2, List2, N),
    M #= N.

First, note that check/2 is nowhere used in the code, so I omit its definition in the following.

Most general query

When I review Prolog code I know nothing about, I always try the most general query first, where all arguments are variables. Ideally, the answers show me what solutions look like in general.

For example, in your case:

?- count(E, Ls, C).
Ls = [],
C = 0.

This—erroneously—suggests that there is only a single solution of your predicate! This is obviously not intended.

Therefore, as a first step, I recommend you remove the !/0 and change this code to:

count(_, [], 0).

count(El, [El|T], N) :-
    count(El, T, N1),
    N #= N1 + 1.

count(El, [Y|T], N) :-
    El \= Y,
    count(El, T, N).

With this change applied, we get:

?- count(E, Ls, C).
Ls = [],
C = 0 ;
Ls = [E],
C = 1 ;
Ls = [E, E],
C = 2 ;
Ls = [E, E, E],
C = 3 .

This looks much better: We now get several valid answers.

Termination

How many answers might this predicate yield? In particular, are there only finitely many answers? If so, we would expect the following query to terminate:

?- count(E, Ls, C), false.

You can try it, and see that it in fact doesn't terminate (at least not very soon). That's a good sign, because from a correct implementation of count/3, we expect nontermination in the most general case!

Completeness

Ideally, we would like the predicate to be complete: It should not omit valid answers.

For example:

?- count(E, [X,Y,Z], C).
E = X, X = Y, Y = Z,
C = 3 ;
false.

Are these really all solutions? I don't think so! Certainly there are lists of length 3 that are different from [E,E,E].

And, in fact, your program also "knows" about them in a sense:

?- count(E, [1,2,3], C).
E = C, C = 1 ;
false.

But again, these are certainly not all cases! Where are the answers in which E is different from 1?

You are facing these issues because you are using the non-monotonic (\=)/2 predicate. This has a few very hard to understand properties, especially if you are currently only beginning to learn Prolog. For example:

?- X \= Y, X-Y = a-b.
false.

?- X-Y = a-b, X \= Y.
X = a,
Y = b.

I recommend to use dif/2 instead to denote that two terms are different, obtaining the following version:

count(_, [], 0).

count(El, [El|T], N) :-
    count(El, T, N1),
    N #= N1 + 1.

count(El, [Y|T], N) :-
    dif(El, Y),
    count(El, T, N).

Wit this version, we get:

?- count(E, [X,Y,Z], C).
E = X, X = Y, Y = Z,
C = 3 ;
E = X, X = Y,
C = 2,
dif(Y, Z) ;
E = X, X = Z,
C = 2,
dif(Z, Y) ;
etc.

And, in particular:

?- count(E, [1,2,3], C).
E = C, C = 1 ;
E = 2,
C = 1 ;
E = 3,
C = 1 ;
C = 0,
dif(E, 3),
dif(E, 2),
dif(E, 1).

This covers all cases that can arise!

Fair enumeration

Since the predicate is pure and monotonic, we can use it to fairly enumerate answers by iterative deepening.

For example:

?- length(Ls, _), count(E, Ls, C).
Ls = [],
C = 0 ;
Ls = [E],
C = 1 ;
Ls = [_G588],
C = 0,
dif(E, _G588) ;
Ls = [E, E],
C = 2 ;
Ls = [E, _G597],
C = 1,
dif(E, _G597) .
C = 2 ;
etc.

That's pretty nice, and shows that we can use this as a true relation, not only for counting, but also for generating.

Hence, you may consider a predicate name that more appropriately describes what this predicate means in general. I leave this as an exercise.

Tail recursive version

Note that since you are using pure predicates, you can freely reorder your goals, and make your predicate tail recursive, yielding:

count(_, [], 0).
count(El, [El|T], N) :-
    N #= N1 + 1,
    count(El, T, N1).
count(El, [Y|T], N) :-
    dif(El, Y),
    count(El, T, N).

Determinism

We currently still have for example:

?- count(a, [a,a,a], Cs).
Cs = 3 ;
false.

Using if_/3, you can improve determinism of this predicate:

:- use_module(library(reif)).

count(_, [], 0).
count(E, [L|Ls], N) :-
        if_(E=L, N #= N1 + 1, N #= N1),
        count(E, Ls, N1).

This makes your predicate at least amenable to indexing. Whether you improve determinism in such cases depends on your Prolog system's indexing mechanism.

mat
  • 40,498
  • 3
  • 51
  • 78
1

You are using predicates that are called moded because they can only be used in very specific situations. In particular, (is)/2 cannot be used as a relation as you need it here.

One way to solve this is to use more general predicates instead. For example, when reasoning over integers, consider using your Prolog system's CLP(FD) constraints, which work in all directions.

For example, with GNU Prolog, you can make the error go away if you simply replace (is)/2 by (#=)/2:

count(_, [], _).

count(El, [El|T], N) :-
    N1 #= N + 1,
    count(El, T, N1).

count(El, [_|T], N) :-
    count(El, T, N).

check(List1, List2) :-
    count(3, List1, M),
    count(2, List2, N),
    M #= N.

Now we get:

?- count(3, [3, 4, 2], C).
C+1#=_1204 ;
true ;
false.

(or, depending on your Prolog system, an equivalent answer).

Why? Obviously, the program is a bit flawed.

I leave finding the mistake as an exercise. Hint: M #= N looks suspicious: This is true iff M is equal to N...

mat
  • 40,498
  • 3
  • 51
  • 78
  • I guess #= in SWI-prolog counts as comments, need to check it. Edited answer to update, which prolog I'm using. – user980952 Nov 17 '16 at 12:05
  • 1
    In SICStus Prolog, YAP and SWI, you need to add `:- use_module(library(clpfd)).` at the beginning to use declarative integer arithmetic. – mat Nov 17 '16 at 12:07