Search code examples
prologprolog-setof

Using a self-created list in prolog


I'm pretty new to Prolog, Don't be too hard on me. Anyhow, I've got the next problem in Prolog:

I've created a small 'database' of actors, defined by:

actor(ID, Name).

Same goes for movies,cast,Director, Defined by:

movie(ID,Name, Director, Category).
director(ID,Name).
cast(MovieID, ActorID).

Now i need to Write a procedure people(Movie, List) that defines the relation between movie name and list of names of all people participated in the movie - director and actors (in any order).

So I've started by:

people(Movie, List):-
    movie(MovID,Movie,_,_,_),   %Getting the movie ID.
   helper(MovID,List).

% Either its a director.
helper(MovID,[Y|Ys]):-
   director(DirID,Y),
   movie(MovID,_,DirID,_,_),
   helper(MovID,Ys).

 % Or an actor
 helper(MovID,[Y|Ys]):-
   actor(ActID,Y),
   cast(MovID,ActID),
   helper(MovID,Ys).

 % Finished when reached to an empty List.
 helper(MovID,[]).

So what the above does is answering whether a given list is acting [or Directing] in that movie. [But it has a problem as well, Since a list which doesn't contain -all- the actors will still get 'true'.]

Anyhow, I thought a bit more about my solution, And I think that it's logically not the perfect approach to that question [That's why i didn't finish it].

I thought about trying another approach, And it's by logically asking: What do i actually need to answer about?

And the answer is: Is there a list so that appending the list of actors in the movie with the director [There's only one] equals to it.

But now I'm pretty much stuck. How can i use a self-created lists in Prolog? Getting the list of actors is easy and it's by doing:

  cast(MovieID,_).

But how can i use the answer defined by the function? [Can i even do that?] Or shall i try to change my approach again?


Solution

  • The relation people(Move, Persons) is, given your database, no longer a monotonic relation. For, would you add facts to actor/2 and cast/2, the relation people/2 would now succeed for a longer and different list of Persons.

    So, given that, you can define helper/2 with setof/3. In fact, you need setof/3 or some other non-monotonic construct to that end:

    helper(MovID, Ys) :-
       setof(Y, person_in(Y,MovID), Ys).
    
    person_in(Y, MovID) :-
       movie(MovID, _, DirID, _, _),
       director(DirID, Y).
    person_in(Y, MovID) :-
       cast(MovID, ActID),
       actor(ActID, Y).
    

    Alternatively, you might put all of it into one big goal. Be aware that in such a situation all existential variables have to be declared:

    helper(MovID, Ys) :-
       setof(Y, Ex1^Ex2^Ex3^DirId^ActID^
                  ( move(MovID,Ex1,DirID,Ex2,Ex3), director(DirId,Y)
                  ; cast(MovID, ActID), actor(ActId, Y)
                  ),
             Ys).
    

    It is very easily possible to forget a variable or two, so you might also use library(lambda) to handle such variables implicitly. This is particularly useful for anonymous variables:

    helper(MovID, Ys) :-
       setof(Y, {MovID,Y}+\
                  ( move(MovID,_,DirID,_,_), director(DirId,Y)
                  ; cast(MovID, ActID), actor(ActId, Y)
                  ),
             Ys).