Victor Blaga
Victor Blaga

Reputation: 1862

Prolog count the number of times a predicate is true

I want to count the number of times a custom predicate is true. For example, I have the following code:

is_man(john).
is_man(alex).
?:-is_man(X).

X will return john, then if I press semicolon it will also return alex, then false.

I want to build something like:

count(is_man(X), Count).

And this to return

Count = 2

How can I do that?

Upvotes: 35

Views: 42422

Answers (3)

Kaarel
Kaarel

Reputation: 10672

In SWI-Prolog:

aggregate_all(count, is_man(X), Count).

NB. the first argument count is one of the special templates:

The Template values count, sum(X), max(X), min(X), max(X,W) and min(X,W) are processed incrementally rather than using findall/3 and run in constant memory.

Upvotes: 42

尾崎隆大
尾崎隆大

Reputation: 158


count(P,Count) :-
        findall(1,P,L),
        length(L,Count).

Upvotes: 10

hardmath
hardmath

Reputation: 8823

For an ISO standard Prolog solution, you might use findall/3 to produce a list of all solutions, then set Count to the length of the resulting list. It could be a bit tricky to wrap this into a user-defined predicate count/2 as you propose, because we need to form the first argument of findall/3 in a way that accounts for any free (unbound) variables in the goal you want to pass as the first argument of count/2.

Many Prologs provide for "counters" or other forms of mutable global values, a nonstandard extension, that could be used in connection with a failure driven "loop" to make the same count. Slightly more cumbersome but sticking to the letter of the Prolog standard would be to use assert and retract to create your own "counter" by adjusting a dynamic fact.

An illustration of the latter approach follows. Making it "multithread safe" would require additional logic.

count(Goal,_) :-
    setGoalCount(0),
    call(Goal),
    incGoalCount(1),
    fail.              /* or false in some Prologs */
count(_,Count) :-
    getGoalCount(Count).

setGoalCount(_) :-
    retract(getGoalCount(_)),
    fail.
setGoalCount(X) :-
    assert(getGoalCount(X)).

incGoalCount(Y) :-
    retract(getGoalCount(X)),
    !,
    Z is X + Y,
    assert(getGoalCount(Z)).

Upvotes: 13

Related Questions