Search code examples
prologprolog-anonymous-variable

Prolog: Matching One or More Anonymous Variables


[_, [ X , _ ],_] will match a list like [d, [X,a], s]. Is there a way to match it to any pattern where there is one or more anonymous variables? ie. [[X,a],s] and [[d,a],[p,z], [X,b]] would match?

I am trying to write a program to count the elements in a list ie. [a,a,a,b,a,b] => [[a,4],[b,2]] but I am stuck:

listcount(L, N) :-  listcountA(LS, [], N).
listcountA([X|Tail], [? [X, B], ?], N) :- B is B+1, listcountA(Tail, [? [X,B] ?], N).
listcountA([X|Tail], AL, N) :- listcountA(Tail, [[X,0]|AL], N).

Thanks.


Solution

  • A variable match a term, and the anonimus variable is not exception. A list is just syntax sugar for a binary relation, between head and tail. So a variable can match the list, the head, or the tail, but not an unspecified sequence.

    Some note I hope will help you:

    listcount(L, N) :- listcountA(LS, [], N).

    In Prolog, predicates are identified by name and num.of.arguments, so called functor and arity. So usually 'service' predicates with added arguments keep the same name.

    listcountA([X|Tail], [? [X, B], ?], N) :- B is B+1, listcountA(Tail, [? [X,B] ?], N).

    B is B+1 will never succeed, you must use a new variable. And there is no way to match inside a list, using a 'wildcard', as you seem to do. Instead write a predicate to find and update the counter.

    A final note: usually pairs of elements are denoted using a binary relation, conveniently some (arbitrary) operator. For instance, most used is the dash.

    So I would write

    listcount(L, Counters) :-
        listcount(L, [], Counters).
    
    listcount([X | Tail], Counted, Counters) :-
        update(X, Counted, Updated),
        !, listcount(Tail, Updated, Counters).
    listcount([], Counters, Counters).
    
    update(X, [X - C | R], [X - S | R]) :-
        S is C + 1.
    update(X, [H | T], [H | R]) :-
        update(X, T, R).
    update(X, [], [X - 1]).  % X just inserted
    

    update/3 can be simplified using some library predicate, 'moving inside' the recursion. For instance, using select/3:

    listcount([X | Tail], Counted, Counters) :-
        ( select(X - C, Counted, Without)
        ->  S is C + 1
        ;   S = 1, Without = Counted
        ),
        listcount(Tail, [X - S | Without], Counters).
    listcount([], Counters, Counters).