Search code examples
prolog

How to read from a list in GNU prolog?


So I have an assignment where I am to produce compatible meeting times between 3 different people. In the prolog file where I define predicates, there is a line given that has the name of the three people I am supposed to compare that reads as follows:

people([ann,bob,carla]).

Where we are supposed to match these names from a data file that defines facts, where a fact holds the following format:

free(ann,slot(time(7,0,am),time(9,0,am))).

My question is, how do I read through 'people' so that I can match names against each other?

My text book doesn't really explain prolog too well, and I am confused on what 'people' actually is (when I say what it actually is, I mean is 'people' a list? an array?) so I am having troubles even searching for a solution as to how to read through each name so I can compare them.


Solution

  • people([ann,bob,carla]). is a fact. The predicate people/1 holds a list of people names. In prolog you have different ways to get elements from a list.

    The most "dirtiest" version is just to write the list with a fixed number of elements:

    ?- people([P1,P2,P3]).
    P1 = ann,
    P2 = bob,
    P3 = carla ;
    false.
    

    You should not do this, because it works only for sets of 3 people and you would have to alter your code everytime a person leaves/enters.

    Normally you go through a prolog list where you just get the first element Head and the rest of a list Tail:

    ?- people([Head|Tail]).
    Head = ann,
    Tail = [bob, carla] ;
    false.
    

    By redoing this you can traverse through the whole list until the list has only one element left. To do this you need a help predicate, which I named person. person takes as first element a List and as second a variable (or a name for test). It unificates the variable with one element from the list:

    person([H|_], H).
    person([_|T], P):-
        person(T, P).
    
    ?- people(L), person(L,P).
    
    L = [ann, bob, carla],
    P = ann ;
    
    L = [ann, bob, carla],
    P = bob ;
    
    L = [ann, bob, carla],
    P = carla ;
    
    false.
    

    It works as follows: you have a list and imagine you see the first element from it only. You have two choices here: first you are ok with just taking the head element as an output, so the second attribute should be the exact same as the head element: person([H|_], H).
    Or second: you ignore the head element and try to find something in the rest of the list by just calling the predicate again with a smaller list: person([_|T], P):- person(T, P).
    When a variable starts with an underscore _ you are not interested in its content.

    Also worth knowing: there are (most likely) inbuild helper predicates such as member/2 which give you back any member of a list:

    ?- people(L), member(P,L).
    

    will give you any person in L.

    To access a single timeslot for a choosen person you simply ask for the predicate free with your person from the list:

    ?- people(L), member(P,L), free(P,S).
    

    If you want to find a timeslot where all persons in the list have to participate you need to define a helper predicate. I named it hastime

    hastime([],_).
    hastime([H|L], S):-
        free(H,S),
        hastime(L,S).
    

    The output of ?- people(L), free(_,S), hastime(L,S). will give you a timeslot S where everone has time. Before calling hastime/2 you guess a Timeslot S. hastime/2 will look if all of the people have time on S: if there are no people (empty list []) you can accept any timeslot (_). If there are at least one person H in your list: ask if H has time on timeslot S and try if the other people from the list have this timeslot S free as well by calling the predicate for the tail list.
    If prolog choose a slot where not all of them have time, it will go back to the point where it choosed the timeslot S and will look for a different value and try again. If there are no such timeslots it will return false.
    Also hastime/2 can be used to find a timeslot by itself, but using it as a "generator" and test at the same time is a bit confusing.