COMP9414 Laboratory Exercise - Weeks 2 - 5

Introduction to Prolog Programming

Objectives

In this Lab, you will practice:

References

Assumptions

These laboratory exercises assume that you have already configured your account on the CSE Unix systems. It also assumes that you have some familiarity with using the Unix shell. In particular you should be familiar with creating and viewing files and directories. You can find information on these things in "The Basics" section from the "Unix Primer".

Introduction

This is a series of laboratory exercises to last the next four weeks (weeks 2 - 5). The primary aim is for you to familiarise yourself with the process of creating and using simple Prolog programs. This exercise will simply ensure that you've understood the lecture material and are capable of writing a small program and getting it to run. You should keep a copy of the "Unix Primer" and the "Introduction to Prolog" course notes handy, in case you get stuck and the instructions here don't help you work things out. There is also "The Prolog Dictionary" online to explain any Prolog terms you are not familiar with.

Instructions

    Accessing Prolog

  1. Log on to the CSE computer system in the normal way, by entering your user name and password (as per the "Unix Primer"). Open an xterm window to perform the lab in. All commands in this lab should be entered in that window.

    This lab will create a number of files. If you don't want them to go directly in your home directory, then create a new directory and cd to the new directory. For example:

        % mkdir ai_lab
        % cd ai_lab
    

    NOTE! Whenever we show you an example session on the computer, what the system types to you will be in "this font", and what you are supposed to type in response will be in "italic font". You do not need to type the percent sign (%) shown at the start of these lines. In this situation, the percent sign represents the Unix shell prompt. (It can also mark the start of a "comment", inside a Prolog program.)

    The first command created a directory called ai_lab, the second command made ai_lab your current directory. Next time you log in you will not need to create the directory... so just change directories using the command, cd ai_lab.

  2. First of all we will make sure you can run Prolog. We will start Prolog, directly enter some facts and queries, then quit.

    Start prolog using the following command:

       % prolog
       Welcome to SWI-Prolog (Multi-threaded, Version 5.6.58) or later
       Copyright (c) 1990-2008 University of Amsterdam.
       SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software,
       and you are welcome to redistribute it under certain conditions.
       Please visit http://www.swi-prolog.org for details.
    
       For help, use ?- help(Topic). or ?- apropos(Word).
    
       ?- 
    
    The ?- is the Prolog prompt. It indicates Prolog is ready to accept input from the keyboard.

    NOTE! If at any time you make a mistake and you want to get a fresh Prolog prompt, you can send the interupt signal, control-C, then type a for abort.

    Quitting Prolog

    To exit from Prolog, type control-D at a ?- prompt. You may have to read the previous note to find out how to get a prompt.

       ?- control-D
       % halt
    

    When we quit Prolog, (by typing control-D at a ?- prompt, the facts we have placed into memory are lost.

    WARNING to Windows/DOS users! You should not try to quit SWI prolog using control-Z. On the Unix systems, this will suspend the Prolog process and leave it in the background. It does not quit the program! (However, this may be different on systems using DOS or Microsoft Windows.)

    Simple Facts

  3. We will enter some simple facts about 3 toy blocks called a, b and c. We will record information on their colour. There are several ways to get this information into Prolog. I shall describe one way for the moment: preloading the data from a file that you had previously created, using the -s "switch" on the prolog command.

    Create a file with the fact(s) you want to load into Prolog in it, using your favourite text editor (like emacs). Let's assume you call your file data1 and that it has the fact colour(a, red) in it:

      % cat data1
      colour(a, red).
    

    We now start Prolog using the following form the the Unix prolog command:

      % prolog -s data1
      % data1 compiled 0.00 sec, 2,164 bytes
      ... followed by usual welcome message ...
    
      ?- 
    

    The second % starts a message from Prolog about the processing of the file data1. Prolog now stores the fact that is in data1 in its memory (often called its database in this context).

    The fact that you entered is supposed to indicate that block a is red.

    The full stop (.) at the end of each line of the file data1 indicates to Prolog that this is the end of a fact (or clause). If you leave out the full stop, Prolog will become confused. It will issue an error message something like the following

    ERROR: /import/.../yourlogin/data1:1: Syntax error: Unexpected end of file
    % data1 compiled 0.00 sec, 2,356 bytes

    If this happens, type control-D to exit Prolog, and have a look at your data1 file, with emacs or whatever. Find the mistake, and fix it. Then start Prolog again, with prolog -s data1 as before.

  4. We can now ask Prolog if it can prove that block a is red by asking the following query.
       ?- colour(a, red).
       
       true.
    
    Prolog attempted to prove this goal by checking its memory.

    The reason Prolog can prove a is red is that we asserted that fact in the previous step. We can ask if Prolog can prove that block b is blue.

       ?- colour(b, blue).
       
       false.
    

    It cannot prove it as we have not told Prolog this fact. We now exit Prolog (by typing a control-D), and add a line with colour(b, blue). on it to the file data1. Then we restart Prolog:

       % prolog -s data1
       ... messages and welcome ...
       ?- colour(a, red).
    
       true.
       ?- colour(b, blue).
    
       true.
    
    Now Prolog can prove that block b is blue.

    We will assume that block c is painted half red and half blue. That is, it is both red and blue. For this we need to add two facts to file data1:

       colour(c, red).
       colour(c, blue).
    
    Quit Prolog, edit data1, restart Prolog, and use the following queries to test this.
       ?- colour(c, red).
       ?- colour(c, blue).
    

    Variables

  5. A query can contain variables which represent terms that are not known by the user. For example, to find the colour of block a, we can use the query...

       ?- colour(a, X).
    

    Here, X, is a variable. The name of a variable must begin with a capital letter or an underscore character, "_".

    Prolog responds by finding a posible value for X where the goal can be proven.

      X = red
    

    After finding this binding, Prolog waits for the user to type either a <return> or a semicolon (;). If the user types a <return>, Prolog stops looking for bindings that make the query provable, and says true.. If the user types a semicolon, Prolog looks for more bindings, and if it can't find any, as here, responds false.. Try it one way, then repeat the query and try it the other way.

  6. To find all blocks which are coloured red, we could use the query...

       ?- colour(Block, red).
       
       Block = a ;
       
       Block = c ;
    
       false.
    

    Remember - you have to type the semicolons. Here the goal can be proven for two different values of the Block variable.

  7. If you ask a query with variables, and Prolog can't prove the goal for any value of the variables, then Prolog will respond with "false." just as before. Try this to find the colour of block d...
       ?- colour(d, X).
    

    Getting the Facts

  8. This next exercise will use the prepared Prolog program family.pl. Take a copy of the family.pl program:
       % cp /home/cs9414/public_html/Labs/family.pl family.pl
    

  9. View the copied file using your preferred text viewer (e.g. cat, view or more). The file shows a list of facts which represent a family tree. For your own benefit, you should draw a diagram of the family tree represented in the program and annotate each node with gender and date-of-birth information.

    Running Queries

  10. Start SWI prolog with the family.pl program. (From now on we'll leave out Prolog's welcome message completely.):

       % prolog -s family.pl
    
       ?-
    

    Remember, if you make a mistake and you want to get a fresh Prolog prompt (:), you can send the interupt signal, control-C, and then typing a for abort. You can quit SWI prolog by sending the end-of-file signal, control-D, at the prompt.

  11. Prolog will read in the family.pl program and prompt you for input. Ask Prolog if Albert is the parent of Peter by entering the query:
       ?- parent(albert, peter).
    
    Prolog responds with
       true.
    
    to indicate that the goal was proven (as it was contained in the family.pl program).

  12. Ask Prolog if Albert is the parent of Brian by entering the query:
       ?- parent(albert, brian).
    
    SWI prolog responds with
       false.
    
    to indicate that the goal could not be proven from the family.pl program.

  13. If you make the mistake of not ending your query with a full-stop, then Prolog will issue a | and will then wait until some more stuff is entered. Try this now:
        ?- parent(albert, jim)
    
    SWI prolog will just respond with...
        |
    
    ...until you enter the full-stop ...
        | .
    
        true.
    

    Queries with Variables

  14. To ask, "Who are the parents of Brian?", enter the query:
       ?- parent(X, brian).
    
    Here, X, is a variable which stands for an object which the questioner does not know about yet. Remember, the name of a variable must begin with a capital letter or an underscore character, "_".

    Prolog replies with:

        X = jim ;
    
        X = pat ;
    
        false.
    
    This is a listing of all values of X where the goal can be proven.

  15. If you make the mistake of not using a capital letter, then Prolog will treat it as a constant. Try this to see:

       ?- parent(x, brian).
    

    This is an occasional source of errors in submitted assignments :-(

  16. You may also make the mistake of capitalising an intended constant. Try this to see:
       ?- parent(albert, Brian).
    
        Brian = jim ;
    
        Brian = peter ;
    
        false.
    
    Here, Brian is a variable and there are two solutions to the query. Work out (in your head, or on paper) what Prolog should respond with when given the query:
       ?- parent(Albert, Brian).
    
    then run the query to check your answer.

  17. The special variable, _, can be used when the value of the variable is not important. Values bound to the variable are not printed, and different appearances of _ are treated as separate variables. Try the following.

    Is Albert a parent...

       ?- parent(albert, _).
    
    Who are the parents...
    
    ?- parent(Parent, _).
    What does this one mean?
    
    ?- parent(_, _).

  18. Recall from lectures that each _ variable is treated as a distinct variable. Try the query
    ?- parent(irene, _), parent(_, brian).
    
    Press the ";" key each time Prolog stops after printing a response. You should see 4 true responses and then false. This means that Prolog has found four ways to satisfy the query, because the two _ variables are treated as distinct. No variable bindings are printed because bindings of _ variables are not reported by Prolog. Thus the query above is similar to:
    ?- parent(irene, X), parent(Y, brian).
    
    which gives 4 solutions (try it) - except that the bindings are reported in this X, Y version of the query.

    If we had instead done the query

    ?- parent(irene, X), parent(X, brian).
    
    we would get a single answer:
    X = jim ;
    false.
    

  19. Sometimes the order of the terms makes a difference. Try these two queries:
    ?- parent(pat, X), parent(pat, Y), X \= Y.
    
    ?- X \= Y, parent(pat, X), parent(pat, Y).
    
    One of them succeeds but not the other. There are too many possible values of X and Y for which X is not equal to Y. Earlier versions of Prolog would get stuck in an infinite loop testing all the options. The current version of SWI-Prolog avoids the infinite loop, but in doing so it refuses to process the query and therefore fails to find any answer.

    Conjunctions

  20. We now ask, "Is Irene a grandparent of Brian?" This can be answered by finding out if Irene is the parent of someone who is the parent of Brian. Enter the following query to ask that question:

        ?- parent(irene, P), parent(P, brian).
    

    The variable, P, represents that "someone" who is Brian's parent and Irene's child. Prolog will show all values for P, where the query is true.

  21. Find all the grandchildren of Irene using the following query:
        ?- parent(irene, Child), parent(Child, GrandChild).
    
        Child = jim
        GrandChild = brian
    
        Child = peter
        GrandChild = lee
    
        Child = peter
        GrandChild = sandra
    
        Child = peter
        GrandChild = james
    
        Child = peter
        GrandChild = kate
    
        Child = peter
        GrandChild = kyle
    
    The response shows there are six sets of values where the query is true. The six grandchildren are Brian, Lee, Sandra, James, Kate and Kyle.

    Rules

  22. The previous question can be restated (in English) as a general rule:
    
        One person, Grandparent, is the grandparent of another person, Grandchild, if:
            Grandparent is the parent of Child, and
            Child is the parent of Grandchild.
    

    In Prolog this rule is written as:

        grandparent(Grandparent, Grandchild) :-
            parent(Grandparent, Child),
            parent(Child, Grandchild).
    

    Use your preferred text editor in another window (e.g. emacs or vi) to append the rule to the family.pl file. Don't forget to save your changes.

    Have Prolog read the modified family.pl using:

        ?- consult(family).
    

    The consult command will force Prolog to re-read the family.pl file; it automatically supplies the ".pl". Prolog will now be aware of the changes you have made to the file, and in fact it issues warnings for each Prolog procedure that you have redefined by consulting "family.pl", just to sure you know which procedures (may) have changed.

  23. Try the new rule to ask who are the grandchildren of Irene.
        ?- grandparent(irene, Who).
    
    Use the family tree you drew to confirm the result. If the result is not what you expected, then check your program for typing mistakes.

  24. Try the new rule to ask who are the grandparents of Jenny.
        ?- grandparent(Who, jenny).
    

  25. Now add the following rule to family.pl and re-consult:
        % older(Person1, Person2) :- Person1 is older than Person2
        %
        older(Person1, Person2) :-
            yearOfBirth(Person1, Year1),
            yearOfBirth(Person2, Year2),
            Year2 > Year1.
    

    The first two lines are comments which serve as documentation for the new rule.

    Also note the use of the built-in predicate ">".

  26. The new rule, older, tests whether one person is older than another. Test this new rule by asking who is older than Pat and then who is younger than Darren.

  27. Advanced Exercise: If you found all of the above too easy, try the following. In the family.pl program, add a rule, siblings(Child1, Child2), to return whether two people are brothers or sisters (i.e. they share a common parent). Then add a rule, olderBrother(Brother, Person), to return whether Brother is an older brother of Person.
        ?- siblings(sandra, X).
    
        X = lee ;
        
        X = james ;
        
        X = kate ;
        
        X = kyle ;
    
        false.
        ?- olderBrother(Brother, sandra).
    
        Brother = james ;
    
        false.
    

    Note that siblings does not report Sandra being her own sibling. You may need to read the text book (Bratko) to find out how to do this. The issue deals with equality (or inequality) of two terms. Prolog has several notions of equality which are covered in the text book.

    How does your program work when siblings share more than one parent? For example:

        ?- siblings(jim, X).
    

    Why does this happen? You should be able to explain the reason for this perhaps unexpected result at some point, but you do not need to solve it at this stage.

    Recursion

  28. If we want to find all the descendants of a particular person, we could first find all the immediate descendants using the parent relation. We could then repeatedly find all the descendants of the descendants and so on. In Prolog this is done using recursion.

    We will now add a recursive definition for the rule descendant(Person, Descendant) which succeeds when Descendant is a direct or indirect descendant of Person. First the base case:

        descendant(Person, Descendant) :-
            parent(Person, Descendant).
    
    How would you phrase this rule in English?

    Here is the recursive case:

        descendant(Person, Descendant) :-
            parent(Person, Child),
            descendant(Child, Descendant).
    
    Add the descendant predicate to the family.pl program and re-consult.

  29. Find all the descendants of Irene using the new predicate.

  30. To find the ancestors of Kyle you could use:
        ?- descendant(Ancestor, kyle).
    
    However, as an exercise, write a predicate, ancestor(Person, Ancestor), to do the same thing without using descendant. The predicate ancestor should use only the parent predicate and recursion.

  31. Confirm that ancestor(Person, Ancestor) gives the same results as descendant(Ancestor, Person).

    Structures and Pattern Matching

  32. So far we have only seen terms which are atoms. A term can also be a structure.

    Structures in Prolog are objects that have several components, but are treated as a single object. To combine the components into a single structure, we choose a functor and use it to group the components together. (See also Bratko, section 2.1.3. and section 2.2)

    The following rule, test, takes two arguments. The first is a stucture with three components using functor, f. The rule succeeds if (1) the first and second component of the structure are the same, and (2) the last component of the structure is the same as the last argument.

        test(f(A, B, C), D) :-
              A = B, C = D.
    

    Enter the rule into a file called, say, test.pl, start up Prolog with prolog -s test.pl and try the following queries. Before each query write down what you expect the resultss to be, then try it. If your expectations fail, you should try to understand what is happening. (See also Bratko, section 2.2)

        ?- test(f(1, 1, 2), 2).
        
        ?- test(f(1, 2, 3), 3).
        
        ?- test(f(1, 1, 2), 3).
        
        ?- test(f(1, X, 2), 2).
        
        ?- test(f(1, _, _), 2).
        
        ?- test(f(1, X, 2), Y).
        
        ?- test(g(1, X, 2), Y).
    
        ?- test(f(X, 1, Y, 1), 1).
    
        ?- test(f(X, 1, Y, 1)).
    
        ?- test(f(X, Y, Z), A).
    
        ?- test(f(X, Y), A).
        
        ?- test(X, a).
    

    The Climbing Database

  33. In the next exercise you will create a new Prolog program from scratch and experiment with structures and pattern matching.

  34. We will create a program to store predicates for a rock climbing database. In this database there are particular climbs which have a name and a grade. We also want to track who first ascended the climb and the date they did it.

    Use your text editor to create a new file, climbing.pl, and add the following facts to the file.

        % male(Person)
        % female(Person)
        %
        % The argument term for these predicates is a structure
        % representing a person.  It is in the form:
        % person(FirstName, LastName)
        %
        male(person('Barry', 'Drake')).
        male(person('Jim', 'Fried')).
        female(person('Dot', 'Kanga')).
    
        % climb(Name, Grade, FirstAscentPerson, FirstAscentDate).
        %
        % This predicate records the climb details.
        %
        climb('Happy Go Lucky', 15, person('Barry', 'Drake'), date(11, 9,1996)).
        climb('High n Dry',     16, person('Jim', 'Fried'),   date(11, 9,1996)).
        climb('Roller',         21, person('Barry', 'Drake'), date(15, 9,1996)).
        climb('Naturally',      14, person('Barry', 'Drake'), date(11,10,1997)).
        climb('The Picnic',     10, person('Dot', 'Kanga'),   date(14, 2,1953)).
    

    You should also add some comments at the start of your program describing its purpose, author and date. Comments start with the percent sign (%) and continue to the end of the line.

    Single quotes ' are used here to construct atoms; in SWI Prolog double quotes " are used to build strings. You can read about the differences in the manual.

  35. We will start with two simple queries which show the males and females in the database:

        % prolog -s climbing.pl
        ... blah blah blah ...
        ?- female(Person).
    
        ?- male(Person).
    

    Note that the Person variable gets bound to a structure.

  36. We can access the individual components of a structure by using Prolog's pattern matching ability. The following queries the last names of all the males:

        ?- male(person(_, LastName)).
    

    Recall that the underscore (_) is a special variable where the results are not printed and different instances are not required to match.

  37. We can use this technique to ask who did the first ascent of "Roller":

        ?- climb('Roller', _, person(FirstName, LastName), _).
    

    Notice that in order to find a fact in the database which would answer the question, Prolog performed a quite complex matching operation between the structures in the query and those in the head of the clause.

  38. Write and execute a query to find the climbs first climbed by Barry Drake.

  39. Write and execute a query to find the climbs first climbed by a female. (It is okay if your query returns other information besides the climb name.)

  40. Next we would like to know which climbs were first climbed before "High n Dry". To do this we need to work out how to compare dates. The following predicate tells us when the first date comes after the second.

            later(date(Day1, Month, Year), date(Day2, Month, Year)) :- !,
                    Day1 > Day2.
            later(date(_, Month1, Year), date(_, Month2, Year)) :- !,
                    Month1 > Month2.
            later(date(_, _, Year1), date(_, _, Year2)) :-
                    Year1 > Year2.
    

    Note that the cuts indicate that if Prolog has found a clause head which matches the goal then there is no need to consider the following clauses.

    Add this new predicate to climbing.pl and re-consult.

  41. Test the new predicate by finding those climbs which were first climbed before "High n Dry". (It is okay if your query returns other information besides the climb name.)

    Lists

  42. A very important type of recursive structure is the list. The recursive definition of a list is: a list may be empty or it may be a term which has a head, which can be any term, and a tail which is another list.
    In normal Prolog, the empty list is written [], and the name of the functor for a list, written as a term, is . - so .(Head, Tail) is the term for a list whose head is Head and whose tail is Tail.

    Let's define a predicate, is_a_list, which succeeds if its argument is a list:

        is_a_list([]).
        is_a_list(.(Head, Tail)) :-
            is_a_list(Tail).
    
    All we have done here is to write a predicate which tests whether a particular term is a list or not.

    Using our definition, a list of numbers [a, b, c] would be written using the notation:

    	.(a, .(b, .(c, [])))
    
    How would the following lists be represented using the .(-,-) terminology?

  43. Create a new Prolog program called lists.pl and add the predicate is_a_list. Try the following queries...
        ?- is_a_list([]).
        
        ?- is_a_list(.(a, [])).
        
        ?- is_a_list(.(a, b)).
        
        ?- is_a_list(a).
    

    Explain how Prolog arrived at the results for each query.

  44. Write a pattern matching predicate, head_tail(List, Head, Tail), which will extract the Head and Tail from the List.

    Add this to the lists.pl file and test your program...

        % prolog -s lists.pl
        ... blah blah blah ...
        ?- head_tail(.(1, .(2, .(3, []))), Head, Tail).
    
        Head = 1,
        Tail = [2, 3].
    

  45. A recursive predicate can be written to test an element for membership in a list. Complete the predicate below:

        % base case
        is_member(Element, list(Element, _)).
        
        % recursive case (to be completed!)
        is_member(Element, list(_, Tail))   :-  INSERT CODE HERE
    

    Add the complete code for is_member to lists.pl and re-consult.

  46. Test the is_member predicate
        ?- is_member(1, list(1, list(2, list(3, nil)))).
        true.
        ?- is_member(3, list(1, list(2, list(3, nil)))).
        true.
        ?- is_member(5, list(1, list(2, list(3, nil)))).
        false.
        ?- is_member(nil, list(1, list(2, list(3, nil)))).
        false.
    

  47. Try the following query and explain the results:

        ?- is_member(X, list(1, list(2, list(3, nil)))).
    

  48. Although this notation is consistent with the way Prolog treats all other data structures, it can be rather clumsy at times. Because lists are used so often, most Prolog implementations use the alternative, more convenient notation, [1, 2, 3]. Internally, Prolog still stores the list as if it were entered in the prefix form. To get some idea of how the compact list notation works we'll look at some queries and their responses. First try this one:

    	?- [X, Y, Z] = [1, 2, 3].
    	X = 1 ;
    	Y = 2 ;
    	Z = 3 ;
    

    This query asks Prolog to match (or unify) the two terms on either side of the equals sign. If a variable appears in a position corresponding to an element in the second list then that variable is unified with the element.

  49. Prolog also has a convenient notation for extracting the head and tail from a list. The vertical bar (|) is used to separate the head from the tail. Try the following query:

    	?- [1, 2, 3] = [Head | Tail].
    	Head = 1
    	Tail = [2, 3]
    

  50. Try the following queries and explain the results.
    	?- [1, 2, 3, 4, 5, 6] = [Head | Tail].
    	
    	?- [1, 2] = [Head | Tail].
    
    	?- [1] = [Head | Tail].
    	
    	?- [] = [Head | Tail].
    	
    	?- [Head | Tail] = [[1, 2], [3, 4, [5, 6]], [7, 8], 9].
    	
    	?- What = [a | [b, c, d]].
    	
    	?- What = [a | [b]].
    	
    	?- What = [[a] | [b]].
    	
    	?- What = [a | b].
    

  51. SWI prolog has a built-in predicate, member, which operates in a way similar to is_member above. However (depending on which version of SWI-Prolog you are using) member might only work on [...]-style lists (just as is_member only works on list(...)-style lists). Repeat the membership queries using

    member:
        ?- member(1, [1, 2, 3]).
    
        ?- member(3, [1, 2, 3]).
    
        ?- member(5, [1, 2, 3]).
    
        ?- member([], [1, 2, 3]).
    

  52. Try the following queries using both member (as shown) and the equivalent is_member using our earlier notation. You will need to translate the list representation to our earlier format to use is_member.

       ?- member(X, [a, b, c]).
       
       ?- member(a, List).
    

    In what way do the predicates member and is_member behave differently?

  53. Write a recursive predicate, cons, to concatenate two lists.

        : cons([1, 2, 3], [4, 5, 6], Result)?
        Result = [1, 2, 3, 4, 5, 6]
    

  54. In an earlier exercise we wrote a predicate, descendant, in the family.pl program. Reload that program and test the predicate to remind yourself how it works.

        % prolog -s family.pl
        ... blah blah blah ...
    
        ?- descendant(albert,Descendant).
    

    This query shows all the descendants of Albert.

  55. Prolog provides a predicate, findall, to put all the responses from such a query into a Prolog list. Try the following:

        ?- findall(D, descendant(albert,D), List).
    

    The first argument is a variable which is used temporarily to indicate what should be put in the list (third argument) when solutions are found to the goal (second argument).

  56. Use findall to write a new predicate, children(Parent, ChildList), where ChildList is the list of children of Parent. Then test the new predicate. For example:

        ?- children(irene, Children).
        Children = [jim, peter]
        
        ?- children(peter, Children).
        Children = [lee, sandra, james, kate, kyle]
        
        ?- children(lee, Children).
        Children = []
    

  57. Advanced Exercise: In an earlier advanced exercise we wrote a predicate, siblings(Child1, Child2), to return whether two people are brothers or sisters. Write a new predicate, sibling_list(Child, Siblings), which returns a list of the Child's siblings. Test the new predicate...

        ?- sibling_list(sandra, Siblings).
        Siblings = [lee, james, kate, kyle]
        
        ?- sibling_list(jim, Siblings).
        Siblings = [peter]
        
        ?- sibling_list(brian, Siblings).
        Siblings = [darren]
        
    

    You will need to deal with the problem of duplicated siblings!

    Arithmetic: using is

  58. Arithmetic expressions in Prolog are actually stuctures with infix functors. Try the following expressions:

        ?- Expr = 1 + 2 * 3 + 4.
        
        ?- Expr = (a + 5) / b.
    

    Prolog will not evaluate expressions unless it is explicitly forced to.

  59. The built-in operator, is, will force an arithmetic structure to be evaluated. Try these:

        ?- Expr is 1 + 2 * 3 + 4.
        
        ?- Expr is (1 + 2) * (3 + 4).
        
        ?- Expr is 10.
    

  60. When is is used on a structure which cannot be calculated an error will be printed. For example:

        ?- Expr is (a + 5) / b .
    
        ERROR: Arithmetic: `a/0' is not a function
        
        ?- Expr is 1 + 2 * 3 + X .
    
        ERROR: Arguments are not sufficiently instantiated
    

  61. Add a predicate, listCount(List, Count), to family.pl which will count the elements of the list. This will be a recursive predicate. This base case is for an empty list which has zero elements. The recursive case should add one to the count of the Tail. Write the listCount predicate then try it out:

        ?- listCount([], Count).
    
        Count = 0
        ?- listCount([a, b, c], Count).
    
        Count = 3
        ?- listCount([1, [2, 3], [[4], 5], 6], Count).
    
        Count = 4
    

  62. Use listCount and findall to write a predicate, countDescendants(Person, Count) which calculates how many descendants Person has.

        ?- countDescendants(albert, Count).
    
        Count = 9
    

  63. Advanced Exercise: Write a new version of listCount, called deepListCount which counts all elements in the list and embedded lists. Here is some sample output:

        ?- deepListCount([], Count).
        Count = 0
        
        ?- deepListCount([a, b, c], Count).
        Count = 3
        
        ?- deepListCount([[a, b, c]], Count).
        Count = 3
        
        ?- deepListCount([a, [b, c], [[d], e], f], Count).
        Count = 6
    

That completes the Prolog laboratory exercises.

Solutions

Solutions can be found here (when made available):


CRICOS Provider Code No. 00098G