prolog system to run Prolog programs
prolog error messages and repairing errors
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.
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.
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.)
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.
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).
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.
?- 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.
false."
just as before. Try this to find the colour of block d...
?- colour(d, X).
family.pl.
Take a copy of the family.pl program:
% cp /home/cs9414/public_html/Labs/family.pl family.pl
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.
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.
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).
?- parent(albert, brian).SWI
prolog responds with
false.to indicate that the goal could not be proven from the
family.pl program.
| 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.
?- 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.
?- parent(x, brian).
This is an occasional source of errors in submitted assignments :-(
?- 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.
_, 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(_, _).
_ 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.
?- 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.
?- 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.
?- 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.
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.
?- 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.
?- grandparent(Who, jenny).
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 ">".
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.
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.
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.
?- 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.
ancestor(Person, Ancestor) gives the same results as
descendant(Ancestor, Person).
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).
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.
% prolog -s climbing.pl
... blah blah blah ...
?- female(Person).
?- male(Person).
?- 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.
?- 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.
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.
[],
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?
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.
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].
% 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.
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.
?- is_member(X, list(1, list(2, list(3, nil)))).
[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.
?- [1, 2, 3] = [Head | Tail]. Head = 1 Tail = [2, 3]
?- [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].
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]).
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?
cons, to concatenate two lists.
: cons([1, 2, 3], [4, 5, 6], Result)?
Result = [1, 2, 3, 4, 5, 6]
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.
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).
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 = []
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!
is
?- Expr = 1 + 2 * 3 + 4.
?- Expr = (a + 5) / b.
Prolog will not evaluate expressions unless it is explicitly forced to.
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.
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
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
listCount and findall to write a predicate,
countDescendants(Person, Count) which calculates how many
descendants Person has.
?- countDescendants(albert, Count).
Count = 9
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
Solutions can be found here (when made available):
CRICOS Provider Code No. 00098G