Prolog ensure a rule's return parameters are unique and in canonical order

138 views Asked by At

I have some data declared in a Prolog file that looks like the following:

gen1(grass).
gen1(poison).
gen1(psychic).
gen1(bug).
gen1(rock).

...

gen1((poison, flying)).
gen1((ghost, poison)).
gen1((water, ice)).

...

weak1(grass, poison).
weak1(grass, bug).
weak1(poison, rock).

strong1(grass, rock).
strong1(poison, grass).
strong1(bug, grass).
strong1(poison, bug).
strong1(psychic, poison).
strong1(bug, poison).
strong1(bug, psychic).
strong1(rock, bug).

Note that the data does not define strong1 or weak1 for compound gen1(...). Those are determined by rules which do not contribute to the minimal working example. I mention them because it might be useful to know they exist.

I am trying to find relations between these terms that form a cycle. Here's one sample function:

triangle1(A, B, C) :-
    setof(A-B-C, (
             gen1(A), gen1(B), gen1(C), A \= B, A \= C, B \= C,
              strong1(A, B), strong1(B, C), strong1(C, A)
             ), Tris),
    member(A-B-C, Tris).

This setup does remove duplicates where A, B, and C are in the same order. However, it doesn't remove duplicates in different orders. For example:

?- triangle1(A, B, C),
   member(A, [bug, grass, rock]),
   member(B, [bug, rock, grass]),
   member(C, [bug, rock, grass]).
A = bug,
B = grass,
C = rock ;
A = grass,
B = rock,
C = bug ;
A = rock,
B = bug,
C = grass ;
false.

That query should only return one set of [A, B, C].

I've thought about using sort/2, but there are cases where simply sorting changes the meaning of the answer:

?- triangle1(A, B, C),
   sort([A, B, C], [D, E, F]),
   \+member([D, E, F], [[A, B, C], [B, C, A], [C, A, B]]).
A = D, D = bug,
B = F, F = psychic,
C = E, E = poison .

I also tried < and >, but those don't work on atoms, apparently.

Any thoughts?

(I looked at the similar questions, but have no idea how what I'm doing here compares to what other people are doing)

EDIT: As per comment about minimal working example.

1

There are 1 answers

4
max66 On BEST ANSWER

You can try sorting inside the setof/3 call. So you should avoid the generation of triples in wrong order.

I mean: calling setof/3, instead of

A \= B, A \= C, B \= C,

try with

A @< B, A @< C, B \= C,

This way you impose that A is lower than B and lower than C, you avoid duplicates and maintain correct solutions.

The full triangle1/3

triangle1(A, B, C) :-
    setof(A-B-C, (
             gen1(A), gen1(B), gen1(C), A @< B, A @< C, B \= C,
              strong1(A, B), strong1(B, C), strong1(C, A)
             ), Tris),
    member(A-B-C, Tris).