Search code examples
prologprolog-setof

Prolog multiple predicates in findall/setof


I'm trying to find ancestors of the Greek mythological Muses using the following facts and rules (simplified):

/* parent(A, B) - A is the parent of B */
parent(zeus, calliope).
parent(zeus, clio).
parent(zeus, melpomene).
parent(zeus, euterpe).
parent(zeus, erato).
parent(zeus, terpsichore).
parent(zeus, urania).
parent(zeus, thalia).
parent(zeus, polymnia).
parent(mnemosyne, calliope).
parent(mnemosyne, clio).
parent(mnemosyne, melpomene).
parent(mnemosyne, euterpe).
parent(mnemosyne, erato).
parent(mnemosyne, terpsichore).
parent(mnemosyne, urania).
parent(mnemosyne, thalia).
parent(mnemosyne, polymnia).

parent(kronos, zeus).
parent(rheia, zeus).
parent(oranos, kronos).
parent(gaia, oranos).

The muse and ancestor rules are defined as:

/* A is a Muse if A's parents are Zeus and Mnemosyne */
muse(A):- parent(zeus, A), parent(mnemosyne, A).
/* A is the ancestor of B if A is the parent of B */
ancestor(A, B):- parent(A, B).
/* A is an ancestor of C if A is the parent of B and B is the ancestor of C */
ancestor(A, C):- parent(A, B), ancestor(B, C).

I want to essentially do the following:

/* Get the set of ancestors A of muses B and store in Z*/
setof(A, ancestor(A, muse(B)), Z).

This doesn't work, and returns false. Another variant I tried:

findall(B, muse(B), Muses),
setof(A, ancestor(A, Muses), Z).

But given that I have a lot more parent facts, it just iterates through every possible ancestor bag.

The last variant I tried:

setof(A, ancestor(A, member(X, muse(X)), Z).

but that errors out with:

ERROR: Syntax error: Operator expected
ERROR: setof(A, ancestor(A, member(X, muse(X)), Z)
ERROR: ** here **
ERROR: .

which makes sense.

I want to be able to write the results, such as:

write('The Muses ancestors are: '), write(MuseAncestors), nl.

What am I doing wrong?


Solution

  • To pass a compound goal to a predicate that takes a goal as an argument, just put it in parenthesis:

    setof(X, ( G1, G2 ), Xs).
    

    As to your compound goal: maybe it is better to ask:

    Who are the ancestors of muses?

    ?- muse(M), ancestor(A, M).
    

    You need a conjunction of goals. Any introductory Prolog text should explain what the conjunction means and why ancestor(A, muse(M)) is not what you mean.

    Keep in mind that if you write:

    ?- setof(A, ( muse(M), ancestor(A, M) ), As).
    

    You leave M a free variable, and you will get one set of solutions for each possible binding of this variable. To avoid this, you can tell Prolog explicitly to not try to bind it:

    ?- setof(A, M^( muse(M), ancestor(A, M) ), As).
    As = [gaia, kronos, mnemosyne, oranos, rheia, zeus].