4
votes

Let's say i have some data in this form:

plot('Jurrassic Park",'action')
plot('Jurrassic Park",'drama')
plot('Jurrassic Park",'adventure')
plot('pulp fiction",'retro')
plot('pulp fiction",'crime')
....

where in plot(X,Y) X is a movie and Y is this movies genre.

What i would like to do is query this database and get all the movies that have 3 equal genres. For example if there is another movie that is action,drama,adventure i would like it to be paired with Jurassic park.What i have done so far:

predic(X,Y,Z,Z2,Z3):-
   plot(X,Z), plot(Y,Z), X \= Y,
   plot(X,Z2), plot(Y,Z2), X \= Y, Z\=Z2,
   plot(X,Z3), plot(Y,Z3), X \= Y, Z\=Z3,Z2\=Z3

The problem is that this may return multiple instances of moves (X,Y)(i want it to return just one) because it returns all the possible combinations between Z,Z2,Z3.It will return (Jurassic Park,Jurassic World,action,adventure,drama) and it will also return (Jurassic Park,Jurassic World,action,drama,adventure).

So how could i make my query stop after finding the 1st combination of Z,Z2,Z3 which satisfies the predicates needs? I can't use "!" because i want this query to return all the possible pairs of movies with 3 common genres and not just 1.

2

2 Answers

2
votes

I would suggest to use the standard aggregation setof, basic list processing, and pattern matching:

predic(X, Y, A,B,C) :-
  setof(P, Q^plot(P, Q), Ps),
  append(_, [X|Ys], Ps),
  member(Y, Ys),
  setof(P, P^plot(X,P), [A,B,C|_]),
  setof(P, P^plot(Y,P), [A,B,C|_]).
2
votes

You can enforce order in the terms Zi, by adding constraints Zi @< Zi+1. Here we thus use the (@<)/2 predicate [swi-doc], that checks the standard order on terms:

predic(X, Y, Z1, Z2, Z3) :- 
    plot(X, Z1), plot(Y,Z1),
    X \= Y,
    plot(X, Z2), plot(Y,Z2),
    Z1 @< Z2,
    plot(X, Z3), plot(Y,Z3),
    Z2 @< Z3.

If you are only interested in distinct sets of two movies being similar, you can make use of the distinct/1 meta-predicate:

predic(X, Y) :-
    predic(X, Y, _, _, _).

similars(X, Y) :-
    distinct(predic(X, Y)).