S0rin
S0rin

Reputation: 1293

Prolog: generate a set of all ground terms from a list of predicates

Given the following facts:

female(alice).
male(bob).
male(charlie).
lives(alice,oxford).
lives(bob,oxford).
lives(charlie,cambridge).

I want to generate the set:

[alice,bob,charlie,oxford,cambridge].

I know how to this for a single predicate:

?- setof(X, male(X), S).
S = [bob, charlie].

But I do not know how to generalise this. The aim is to query the program with a list of predicates:

f([male/1,female/1,lives/2]).

I tried to generalise the above 'setof' statements:

g(P,1) :-
  Atom = [P,X],
  Goal =..Atom,
  setof(X, Goal(X, Y), S),
  write(S).

g(P,2).

f([]).

f([H|T]) :-
  H = P/A,
  g(P,A),
  f(T).

But this did not work.

Can anyone help?

Upvotes: 2

Views: 742

Answers (1)

Fred Foo
Fred Foo

Reputation: 363567

You're almost there. Higher-order programming in Prolog can be achieved with the call or apply predicates. Here's a version with call/1, which is standardized by ISO:

get_ground_args(Goal, Args) :-
    findall(X, (call(Goal), Goal =.. [_|GoalArgs], member(X, GoalArgs)), Args).

Example:

?- get_ground_args(lives(_,_), Args).
Args = [alice, oxford, bob, oxford, charlie, cambridge].

Now to tie this in to your original setup, use length/2 to generate the initial arguments inside a nested setof/findall:

all_ground_args(Preds, Args) :-
    setof(X, (findall(Y, (member(Pred/Arity, Preds),
                          length(GoalArgs, Arity),
                          Goal =.. [Pred|GoalArgs],
                          get_ground_args(Goal, GroundArgs),
                          member(Y, GroundArgs)),
                      AllArgs),
              member(X, AllArgs)),
          Args).

Demo:

?- all_ground_args([male/1,female/1,lives/2], Args).
Args = [alice, bob, cambridge, charlie, oxford].

(All of this will probably fail when a predicate has non-ground args.)

Upvotes: 3

Related Questions