Higor Gomes
Higor Gomes

Reputation: 13

How to count the predicates

I'm looking for a way to count the numbers of predicates. Example:

%facts

has_subclass(thing,animal).
has_subclass(thing,tree).
has_subclass(thing,object).

% and I ask

count_has_subclass(thing,X).

% result

X = 3.

Upvotes: 1

Views: 471

Answers (4)

CapelliC
CapelliC

Reputation: 60034

To build a list just to count solutions seems 'old style', but in traditional Prolog there is only the DB (assert/retract) alternative to overcome the 'stateless' nature of computations. Indeed findall/3 and friends builtins could be (naively) rewritten by means of assert/retract. But something have shown up since the early 80's ages :). I illustrate with SWI-Prolog, but - more or less naively - all of them could be implemented by assert/retract. Of course, most Prolog implementations have 'non logical' (or 'imperative', or 'impure') facilities to implement such basic task, without resorting to the heavyweight DB interface - like setarg/3, nb_setval/2 and others...

I digress... library(aggregate) should be shown first:

?- aggregate(count, T^has_subclass(thing,T), C).
C = 3.

This lib is well worth to study, does a lot more than - efficiently - counting... Another, recent, addition is library(solution_sequences). It's not more efficient than setof/3 + length/2, guess, but interesting in its own. Counting is a bit more involved, and uses call_nth/2:

?- order_by([desc(C)],call_nth(has_subclass(thing,T),C)).
C = 3,
T = object ;
...

Some far simpler code of mine, based on nb_setarg/3 (and help from @false):

?- [carlo(snippets/lag)].
true.

?- integrate(count,has_subclass(thing,T),C).
C = 3.

Upvotes: 0

Paulo Moura
Paulo Moura

Reputation: 18683

Using the standard setof/3 is a better option as it allows easy definition of a more general predicate that can enumerate solutions when the class argument is not bound. For example, assume the following database:

has_subclass(thing,animal).
has_subclass(thing,tree).
has_subclass(thing,object).

has_subclass(animal,cat).
has_subclass(animal,dog).

has_subclass(tree,pine).
has_subclass(tree,oak).

And the definition:

subclass_count(Class, Count) :-
    setof(Subclass, has_subclass(Class, Subclass), Subclasses),
    length(Subclasses, Count).

Sample call:

| ?- subclass_count(Class, Count).

Class = animal
Count = 2 ? ;

Class = thing
Count = 3 ? ;

Class = tree
Count = 2

yes

If you try instead one of the findall/3 solutions in the other answers, we get instead:

| ?- count_has_subclass(What, Count).

Count = 7

But note that this solution also have a sensible interpretation as returning the number of all existing subclasses when the class is not specified.

Upvotes: 1

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477533

We can use findall/3 for this, and then use length/2 to obtain the length of the list:

count_has_subclass(What, N):-
    findall(X, has_subclass(What, X), L),
    length(L, N).

If it is however possible that has_subclass/2 yields the same values multiple times for a given key (like thing), then we can use for example sort/2 as a duplicate filter, like:

count_has_subclass(What, N):-
    findall(X, has_subclass(What, X), L),
    sort(L, S),  %% remove duplicates
    length(S, N).

Note that if What is a free variable, then you will count all yeilds of has_subclass(_, _). (optionally with a uniqness filter on the second parameter).

Upvotes: 1

gusbro
gusbro

Reputation: 22585

For facts like your example:

count_has_subclass(What, Count):-
  findall(1, call(has_subclass(What, _)), L),
  length(L, Count).

Upvotes: 2

Related Questions