2. Course Description
During this course we pursue the theme of logic
programming by offering a comprehensive
introduction to the syntax, semantics and features of
Prolog, a well-known logic programming language that
has been used extensively in a wide variety of AI
application areas. The approach will be highly
practical, being organised around a series of carefully
chosen hands-on exercises.
3. Course Textbooks
Main Text:
o David Callear
Prolog Programming for Students
DP Publications Ltd., London.
1994
ISBN: 1-85805-093-6
Other:
o W.F.Clocksin and C.S.Mellish
Programming in Prolog
Springer-Verlag, Berlin.
1994
ISBN: 3-54058-350-5
4. Schedule 1. Introduction
2. Key features of Prolog
3. Further Prolog features
4. More on Rules
5. Evaluation
6. Backtracking
7. Numbers & Maths Operators
8. Basic Lists
9. Advanced Lists
10. Recursion
11. Advanced Recursion with Lists
12. CFG & DFG
5. 1. Introduction
• The Prolog language
• stands for PROgramming in LOGic
• developed to implement predicate calculus in a
computer program
• variety of uses
• Uses of Prolog
• AI applications
• Deductive systems
• Databases
• Image analysis
• Prolog Interpreters
• SWI-Prolog, WINProlog, TurboProlog.
• A simple program
ICS1019 / ICS5003
6. o PROLOG is a 5th. Generation prog. Language,
standing for PROgramming in LOGic and was
originally developed in Edinburgh University
in the 1970s.
o It is mainly used to program for A.I and is
based on a subset of Predicate Calculus (Horn
Clauses).
o Uses of Prolog
o AI applications
o Deductive / Decision support systems
o Databases
o Image analysis
o Natural Language Processing
o etc...
The Prolog Language
7. o Comparing Prolog to Pascal
o PASCAL
o is compiled to an executable file.
o Is imperative - sequence of instructions.
o Program flow - conditionals & loops.
o Has built-in functions.
o Expression evaluates to one of many values.
o PROLOG
o is interpreted.
o Is declarative - series of definitions.
o The HOW of solving the problem is NOT defined, only
what is TRUE or NOT of a certain state of affairs is
described.
o A statement can either be TRUE or FALSE.
o A few built-in rules, and a few fundamental
operations.
The Prolog Language
8. Comparing Prolog to Pascal
o PASCAL
These programs contain a series of
INSTRUCTIONS given to a computer as
PRCOEDURES to perform. The order of
doing things is SPECIFIED and a number of
ACTIONS performed as a consequence.
o PROLOG
On the other hand, these kind of programs
contain a set of FACTS and RULES. Aspects
of the problem which are TRUE or FALSE are
described (defined), rather than the method
to achieve a RESULT.
The Prolog Language
9. o Prolog interpreters
o SWI-Prolog (online & downloadable).
o Quintus.
o Poplog.
o WinProlog.
o TurboProlog.
o LPA MacProlog.
o Recommended Texts
o L. Sterling and E. Shapiro, The Art of
Prolog, 2nd Edition, MIT Press, 1994.
o I. Bratko, Prolog Programming for Artificial
Intelligence, Addison Wesley, 1990, ISBN 0-
201-41606-9
o W. Clocksin and C. Mellish, Programming
in Prolog, Berlin: Springer-Verlag 87.
The Prolog Language
10. o A simple program (program1.pl)
The Prolog Language
student(mark).
student(steve).
student(helen).
student(sylvia).
student(pauline).
student(frank).
female(helen).
female(sylvia).
female(pauline).
male(mark).
male(steve).
male(frank).
clever(helen).
clever(steve).
clever(pauline).
works_hard(helen).
works_hard(mark).
works_hard(frank).
| ?- male(mark).
yes
| ?- male(matthew).
no
| ?- clever(frank).
no
| ?- works_hard(frank).
yes
| ?-
11. o Another simple program(program2.pl)
The Prolog Language
student(marku).
student(pawlu).
student(maria).
student(sylvia).
student(pauline).
student(pietru).
mara(maria).
mara(sylvia).
mara(pauline).
ragel(marku).
ragel(pawlu).
ragel(pietru).
bravu(maria).
bravu(pawlu).
bravu(pauline).
biezel(maria).
biezel(marku).
biezel(pietru).
| ?- ragel(marku).
yes
| ?- ragel(mattew).
no
| ?- bravu(pietru).
no
| ?- biezel(pietru).
yes
| ?-
12. o Yet another simple prog.(program3.pl)
The Prolog Language
parent_of(mary,peter). parent_of(james,peter).
parent_of(mary,helen). parent_of(james,helen).
parent_of(mary,lydia). parent_of(james,lydia).
parent_of(marion,mary). parent_of(tony,mary).
parent_of(marion,mark). parent_of(tony,mark).
female(mary). female(helen).
female(lydia). female(marion).
male(peter). male(james).
male(tony). male(mark).
| ?- parent_of(mary,lydia).
yes
| ?- parent_of(mary,mary).
no
| ?-
13. In everyday life we often express facts about
properties of objects and relations between
objects.
e.g. Blood is red. red(blood).
Summer is hot. hot(summer).
Alison is nice.nice(alison).
similarly facts about relations:-
e.g. James is Peter’s parent
parent_of(james,peter).
Joe was born in Libja
born_in(john,libja).
5 is between 3 and 7
between(5,3,7).
These are called Predicates.
Relations - Facts
14. 1. Extend program3.pl to include 4 additional facts for each predicate
2. Use a Prolog interpreter to test the new facts
3. Create a new program to represent the following facts:
a. A dog is an animal
b. A rose is a flower
c. A dog has 4 legs
d. A person has 2 legs
e. A daffodil is a flower
4. Test the facts that you created in the previous question
Save all your answers in a text file “Ans1.pl” and submit over the VLE.
Exercise 1
15. 2. Key Features
• Relations
• facts
• rules
• example
• Queries - Goals
• simple
• with variables
• example
• Variables
• within goals
• anonymous
• Binding
• pattern matching
• backtracking
ICS1019 / ICS5003
16. To express more complex relations as well as
define new relations in terms of existing
ones, rules are used.
e.g. A person is a son of another if the person
is a male and the other is a parent of this
same person.
son_of(Son,Parent):- male(Son),
parent_of(Parent,Son).
e.g. A person is older than another if he/she
was born before the other.
older_than(X,Y):- born(X,Year1),
born(Y,Year2),
Year1<Year2.
Relations - Rules
17. Worth noting:
Lower case letters are used to represent values
and are called atoms.
Higher case letters are used to represent
variables - discussed later.
Rules express properties or relations that hold
(true) if certain conditions are satisfied - these
are called goals.
The operator “ :- ” is used instead of the English
“if”.
The operator “ , ” is used as an ‘and’ in between
goals of a rule, while a “ ; ” is used as an ‘or’.
All properties, relations and rules must terminate
with a period (“.”).
Relations - Rules
19. | ?- [program4].
{consulting program4.pl…}
{program4.pl consulted, 0 msec 10848bytes}
yes
| ?- student(frank).
yes
| ?- female(frank).
no
| ?-male(frank).
yes
| ?- clever(sylvia).
no
| ?- clever_girl(sylvia).
no
| ?- clever_girl(mark).
no.
| ?- clever_girl(Who).
Who = helen ;
Who = pauline ;
no
Relations - Example
| ?- get_first(Who).
Who = helen ;
no
| ?- pass(steve).
yes
| ?- clever(frank).
no
| ?- pass(frank).
yes
20. After that a program has been consulted we can
ask and query the information within the
program by issuing queries or questions called
goals. The goals will result true or false once
that the interpreter goes through predicates.
e.g. Is Pauline a female? (program1)
female(pauline).
e.g. Is Tony Peter’s parent? (program3)
parent_of(tony,peter).
These are simple goals as the questions are
specific queries on program facts.
To note that the interpreter scans all the
consulted program from top to bottom until
the goal is achieved (yes), or else if not true or
absent, a no is returned.
Queries - Goals
21. More general questions or queries can be
issued to the consulted program by placing
variables within the goal.
e.g. Who are all the female? (program1)
female(Who).
e.g. Who are Peter’s parents? (program3)
parent_of(WHO,peter).
Again in this case the interpreter scans all the
consulted program from top to bottom until
it binds a value to the variable and returns a
‘yes’. If we issue a ‘ ; ’ the interpreter will try
to satisfy the goal again by resuming the
scan. If no bindings are possible a ‘no’ will be
returned. Variables and binding will be
expanded later on.
Queries - Goals
22. Interesting to trace through a complex query with
variables within the goal.
e.g. Who will pass? (program3)
| ?- pass(Which_Student).
Which_Student = steve ;
Which_Student = helen ;
Which_Student = pauline ;
Which_Student = mark ;
Which_Student = helen ;
Which_Student = frank
| ?-
Why was Helen bound twice?
What is backtracking?
Backtracking is the process the interpreter engages in when one of
the predicates on the right hand side goals is not satisfied, and
so it attempts to satisfy again a previous goal in order to satisfy
the current one. Backtracking will be dealt in more detail in
lectures 5 and 6.
Queries - Example
23. Variables have already been defined as one or
more lettered strings with an initial capital
letter. The use of some variables within goals
have also been covered. But what about the
question
Who is a parent? (program3). Basically we don’t
care who the children are.
The Anonymous variable “_ ” is used.
| ?- parent_of(Parent,_).
Parent = mary ;
Parent = james ;
Parent = mary ;
Parent = james ;
Parent = mary ;
Parent = james ;
Parent = marion ;
Parent = tony ;
Parent = marion ;
Parent = tony
| ?-
Variables
24. The process of finding values for the variable
given in a goal involves a search of the
program. Starting from the top it looks for a
fact to match the goal and binds the goal
variable to the name that is argument to the
fact. This bound value is then returned to the
user as a solution to the goal.
If further values are requested, the program
returns to the point at which it last found a
successful match and resumes the program
search from there. The goal variable is at this
point unbound.
The methods that Prolog uses to achieve goals is
called pattern matching of goal to fact.
Sometimes the interpreter has to backtrack in
order to find other solutions which will satisfy
all the goals in the body of a rule.
Binding
25. When Prolog attempts to achieve a goal it checks
every fact and rule head to see if any of them
matches the form of the goal exactly in
spelling and also the number of arguments in
the clause. When the exact pattern of the goal
matches a clause, Prolog equates the
arguments of both sides.
e.g. parent_of(mary,Child). program3
Child = peter;
Child = helen;
Child = lydia
e.g. get_first(Student). program4
Student = helen
because student(helen) is TRUE
and clever(helen) is TRUE
and works_hard(helen) is TRUE too.
Binding - Pattern Matching
26. Backtracking is responsible for what has been called Prolog’s
“relentless search for solutions”; backtracking ensures
that if a solution exists to a goal it is found, and if more
than one solution exists then they are all found.
e.g. get_first(Student). program4
Student=mark binds but clever(mark) is false.
Prolog backtracks to satisfy student(Student)
Student=steve binds and clever(steve) is true, but
works_hard(steve) is false.
Bactracking again to clever(Student) and back to
student(Student) to try again.
Student = helen binds and is TRUE
clever(helen) is TRUE, and
works_hard(helen) is also TRUE.
Goal succeeds for Student = helen. YES.
Binding - Backtracking
27. 1. Extend program4.pl to include a new rule clever_boy
2. Use a Prolog interpreter to test the new rule
3. Extend program3.pl to create new rules to find:
a. siblings(A,B) where A and B have the same parent
b. grandMother(Nanna, Grandchild)
c. sisters(Sister1, Sister2)
d. grandchild(Grandchild, Grandparent)
e. father(Dad, Child)
4. Test the facts that you created in the previous question
Save all your answers in a text file “Ans2.pl” and submit over the VLE.
Exercise 2
28. 3. Additional Features
• Cut
• description
• example
• Not
• possibilities
• example
• Goals
• arity
• conjoined
• multiple occurences
• examples
• Tutorial
• questions and queries
• handout
ICS1019 / ICS5003
29. It is sometimes difficult to stop Prolog from
backtracking and continously searching for
further answers to a question (with repetitive
results). There is in fact a mechanism for
stopping Prolog searching called ‘cut’, which is
represented by the symbol ‘ ! ’. It is, however,
not the easiest of programming constructs to
use in Prolog and it’s not good practice to use a
cut to control your program - so try and avoid
it.
e.g. A rule to return 1 daughter only.
daughter_of(Daughter,Parent):-
parent_of(Parent,Daughter),
female(Daughter),!.
Prolog cannot backtrack past a cut.
Further features - Cut
30. Another operator that is best avoided if
possible is the not. If a clause can be
written in a positive form (i.e. intended to
succeed) than it is easier to read and
understand the rule concerned than if the
clause is intended to fail and is negated to
achieve success.
e.g. Which student will fail in program4.pl
fail(Student):- student(Student),
not(pass(Student)).
Special attention must be taken when using
not or cut.
The most significant problem is that free
variables are not allowed within a not on its
own in a rule, but should be the last of the
goals in a rule as the arguments will by then
usually be bound.
Further features - Not
31. We have already seen that goals on the
right side of a rule (its body) together
with fact and rules are unique depending
on the name and the number of
arguments - arity.
A number of goals conjoined with an ‘and’ (
, ) have to be all satisfied in order for the
rule to succeed.
Instead of using an ‘or’ ( ; ) in between
goals, it is better practice to write
additional rules with the same name and
arity. This is very similar to overloading
functions in C++ and Java.
Further features - Goals
32. A simple addition program (program5)
sum(Sum,X,Y):- Sum is X+Y.
sum(Sum,X,Y,Z):- sum(Sum1,Y,Z),Sum is X+Sum1.
sum(Sum,X,Y,Z,Zz):-sum(Sum1,X,Y),
sum(Sum2,Z,Zz),Sum is Sum1+Sum2.
mmont@zeus>sicstus
SICStus 3 #6: Mon Nov 3 18:32:08 MET 1997
| ?- [program5].
{consulting program5.pl…}
{program5.pl consulted, 0 msec 10848bytes}
yes
| ?- sum(S,2,3).
S = 5
| ?- sum(S,2,3,5).
S = 10
| ?- sum(S,2,3,5,10).
S = 20
| ?-
Lecture 7 will deal fully with numbers and
mathematical operators, whereas the rule
‘sum’ will be written much more efficiently
with lists (lects.8&9)
Further features - Example
33. 1. Consult program3.pl by writing queries to find out:
a. Who are Mary’s parents?
b. Is Mary a parent?
c. Who is a parent?
2. Write new rules to represent:
a. Daughter of a person using daughter_of(Daughter,Parent)
b. Son of a person using son_of(Son,Parent)
c. Mother of a person mother_of(Mother,Child).
d. Brother of a person brother_of(Brother,Person).
e. Grandfather of a person grand_father(Grandad,Grandchild).
f. Granddaughter of a person grand_daughter(Granddaughter,Grandparent).
g. Uncle of a person uncle_of(Uncle,NephewNiece).
Submit all your answers in a file “Ans3.pl”
Exercise 3
34. 4. More on Rules
• Structure
• head
• body
• variables
• placeholders
• anonymous
• syntax
• semantics
• logical operators
• IF :-
• AND ,
• OR ;
• NOT ~
ICS1019 / ICS5003
35. A rule is a convenient way of creating a
handle for an often used complex query,
just as a subroutine is a handle for a
complex set of instructions in a
procedural programming language
Rule syntax: Note that a rule as pointed
out in lecture 2 consists of:
o a left hand side or “head”, which has the
same shape as a fact.
o a right hand side which is identical to a
number of conjoined queries. It
comprises a list of queries or subgoals.
o the symbol :- which seperates both.
More on Rules
36. Rule semantics: The semantics of a rule
must be seen in the context of what
happens when a single query is
processed. The first step is to search the
database for a set of matching clauses,
which might be facts or rules.
NOTE:
1. A rule matches whenever the head of
the rule matches the query in just the
sense explained for a fact, i.e.
o matching predicate name
o matching arguments
o same number of arguments (arity)
More on Rules
37. More on Rules
2. The head of a rule contains variables that
will bind and will be used within the
goals.
3. The rule might succeed and be TRUE,
while also some of the variables in the
head are meant to be placeholders for the
values to be returned. e.g.
sum(Sum,X,Y).
4. Unlike a fact a rule has a right hand side
which specifies a list of subgoals that
must be achieved for the original query to
hold. e.g. sum(S,2,3).
X and Y bind with 2 and 3
respectively, while S with (2+3).
38. /* make(make reference, car make) */
car_make(01,ford).
car_make(02,rangerover).
car_make(03,toyota).
car_make(04,mazda).
car_make(05,daihatsu).
car_make(06,volkswagen).
car_make(07,fiat).
/* type(type reference, type of car) */
car_type(01,hatchback).
car_type(02,van).
car_type(03,off_road).
More on Rules - example6.pl
/* car(make ref, type ref, model, reg no, colour, year) */
car(01,01,escort,fsd127,blue,1990).
car(07,01,uno,tre333,blue,1996).
car(02,03,discovery,uje777,red,1995).
car(03,01,starlet,uij236,green,1991).
car(01,02,transit,ski432,green,1991).
car(07,01,bravo,juy677,red,1998).
car(03,03,hilux,kil989,blue,1991).
car(04,01,three2threeF,kul101,blue,1995).
car(05,01,charade,ill666,white,1991).
car(06,01,polo,ass001,blue,1997).
car(02,03,freelander,hyt456,blue,1998).
car(06,01,beetle,our127,yellow,1985).
car(03,02,hiace,far626,white,1992).
car(07,01,punto,jdu736,white,1996).
39. Consult this program to find out:
o the full details of all the blue cars,
o the registration numbers of the white cars,
o the 1995 models
Write a rule to find out:
o the year of all the off-roads
o the colour of all the fiats
o the model of the 1991 cars which are not white
o the make, registration number and model of all the blue
hatchbacks after 1991
To note:
o pattern matching
o binding
o backtracking
o negation
o variables arity
o use of anonymous variable
o what about a cut?
o any other queries we can make?
o similarity of example to SQL, INGRES in DB.
More on Rules - example6.pl
40. The logical operators are the only expression-level
connectives used in Prolog. These operators are used to
link facts together in the body of a rule, and to link the
body to the head of a rule. The body of the rule is
evaluated in execution by determining the truth of the
constituent expressions, then evaluating the truth of the
whole body using the logical operator rules on the
individual truth values returned. The Prolog operators
are: IFF :-
AND ,
OR ;
NOT not
Note:
o the ‘iff’ is used exclusively in the linking head to body.
o good practice to avoid ‘or’, if necessary write two or more
seperate clauses with same name and arity.
o different interpreters use different operators.
Logical Operators
41. Fancy Stuff
Prolog can also be used to improve the human-computer interaction as demonstrated
in this simple program:
go:- write(‘What is your name? ’),
read(Yourname),
write(‘Hello ’),
write(Yourname),
nl.
go:- write(‘Integers 2 Months’),
nl, write(‘enter month’),
read(Month),
write(‘The Month is ’),
month(Month), nl.
month(1):- write(‘January.’).
month(2):- write(‘February.’).
month(12):- write(‘December.’).
month( _ ):- write(‘…? Incorrect Input.’).
write
read
:-
nl
42. More Fancy Stuff
/* ARITHMETIC PROGRAM */
run:- write(‘Enter a number: ’), read(Num1),
write(‘Another number: ’), read(Num2),
write(‘What operation do you desire?’),
write(‘a. Add’),nl,
write(‘s. Subtract’),nl,
write(‘m. Multiply’),nl,
write(‘d. Divide’),nl,
write(‘e. Exit’),nl,
write(‘Enter a,s,m,d,e:’), read(Option),
choice(Option,Num1,Num2).
choice(a,N1,N2):- X is N1+N2, write(X),nl.
choice(s,N1,N2):- X is N1-N2, write(X),nl.
choice(m,N1,N2):- X is N1*N2, write(X),nl.
choice(d,N1,N2):- X is N1/N2, write(X),nl.
choice(e, _ , _ ).
choice( _ , _ , _ ):- write(‘Wrong Option’),nl.
43. 1. Consult program4.pl and issue the following queries:
a. Will Mark get a first?
b. What about Helen? Will she get a first?
c. Use the operator “;” to find out all those who will pass
2. Add another rule, called second_class(Student), to define a student that will pass but does not get a first
class.
3. Query the new rule to find out all those who will get a Second Class.
Note: It does not matter at this stage if you get same multiple output.
4. Write two more rules and query them appropriately for:
a. Clever boys using clever_boy(Boy).
b. Students who are clever but do not work hard using the predicate what_a_pity_student(Student)
5. Consult program6.pl to find:
a. The registration of the red Range Rover
b. The year of manufacture of the blue cars
Submit all your answers in a file “Ans4.pl”
Exercise 4
c. The make of the yellow beetle
d. The type of cars that were manufactured after 1997
44. 5. Evaluation
• Think Prolog
• program interpretation
• process execution
• Rules & Goals
• evaluating rules
• satisfying goals
• Executing a Goal
• pattern matching
• backtracking
• Pattern Matching
• fact or Rule head
• Backtracking
• Examples
ICS1019 / ICS5003
45. In order to be able to program in Prolog it is very important
to understand how Prolog interpreters execute rules and
goals. This understanding allows the programmer to make
sure not only that they achieve the result they want from a
rule, but also that they get only what they want, and no
more. Understanding the process of execution in Prolog is
vital for understanding how to program correctly
Most importantly, a programmer must be able to:
o mentally trace through the steps taken by an interpreter,
o keep backtracking in mind,
o attempt to terminate the program gracefully always - i.e. a
positive outcome is achieved with all kind of input.
Think Prolog
46. Rules and Goals
It is important to realise that rules and goals are evaluated in exactly the
same way:
A goal that consists of a rule name and argument is an interactive
request for evaluation.
The same request could be issued within a program from a rule that is
being executed.
Note:
• The declarative meaning of Prolog programs is concerned with what
relations are defined in the program. The procedural meaning
determines how the result is obtained.
• A Prolog programmer can concentrate on the declarative way of
thinking and may leave most of the procedural details to the Prolog
interpreter.
47. Questions or queries force the execution of Prolog
programs. Prolog tries to satisfy a goals in a question
from left to right.
It does this by matching it with the head of a clause and
further by trying to satisfy goals in the body of that
clause. In order to find a matching clause, Prolog
searches a program in top-down manner.
If a goal can not be satisfied, Prolog automatically
backtracks to the last goal that was successfully
matched and tries to match it with the head of some
other clause.
Executing a Goal
48. When searching for a clause, Prolog continues searching from
the clause with which the goal was previously matched.
When backtracking, variables are uninstantiated, i.e. loose
the values they acquired in the last match.
Prolog assumes that everything is stated in the program or
can be derived from the program. This property is referred
to as the ‘closed world assumption’.
Negation in Prolog succeeds if the negated goal fails. This is
referred to as negation as failure. Negation in Prolog can
not instantiate any variable in the negated goal.
Executing a Goal
51. ?- student_options(Who,Which).
Who = liam_gallagher
Which = pascal ;
Who = brandy
Which = cobol ;
Yes
?- student_options(celine_dion,CC).
CC = java ;
CC = pascal ;
CC = logic ;
No
?-
Example - program7.pl
52. ?- student_options(Who,prolog).
Who = bjork ;
Who = mike_tyson ;
No
?-
?- students_in_course(prolog).
bjork
mike_tyson
Yes
?- student_marks(mark_morison).
Marks for student mark_morison
java --> 35
cobol --> 40
pascal --> 85
Yes
?-
Example - program7.pl
53. 1. Consult program7.pl and issue the following queries:
a. Who are all the male students?
b. Which female students are taking which courses?
c. What are the dates of birth of those students attending the Graphics course?
2. Add the following rules:
a. Publish the students’ names and respective marks for a particular topic
b. What is the name and age of those students who got an “A”?
c. Which students are paying more than €300 for a course?
d. Which courses are taken by students who were born before 1975?
3. Query and test the new rules.
Submit all your answers in a file “Ans5.pl”
Exercise 5
54. 6. Backtracking
ICS1019 / ICS5003
• Complex Goals
• Graceful termination
• debugging
• tracing
• stepping
• Examples employing several features
like:
• multiple pattern matching
• nested backtracking
• cut and not
• Tutorial
• questions and queries
• handout
55. Complex Goals
In the same way that a rule can consist of a number of clauses to be
evaluated, so can a goal. This is less often used in practice, but is still
possible.
For example, suppose we want to know which parents have at least two
children in program3. To do this we can enter a complex goal:
?- parent_of(Child1,SomeParent),
?- parent_of(Child2,SomeParent),
?- Child1 == Child2.
This goal ensures we get the data we want by using the same variable for
the parent’s name, different variables for the children’s names, and a
check that the two child variables are not bound to the same value.
A rule with same goals yields same results.
56. Complex Goals
If we end the first clause in the goal with a comma, the interpreter realises that we wish
to AND a second clause on to the goal, and so provides a second line for input. The
same is true for the second line, the interpreter does not try to evaluate the goal until
it reads a full stop, at the end of the third clause in our case.
When this goal is executed, the interpreter has to repeatedly search the entire list of
parent_of clauses. It starts by binding values to Child1 and SomeParent, then goes
on to try and satisfy the second parent_of. To do this it starts again at the top of the
clause list. This means that Child2 binds to the same value as Child1. However,
returning a result is prevented here by the test Child1 == Child2.
57. Complex Goals
The interpreter than backtracks, freeing the most recent binding
(Child2) and searching for alternate solutions until it succeeds.
A goal that is used frequently is expressed in the form of a rule and its’
head used as a goal or query obtaining the same outcome every time.
So, the previous goal can easily be re-written as :
two_child_parent(Who) :-
parent_of(Child1,Who),
parent_of(Child2,Who),
Child1 == Child2.
58. Graceful Termination
As in any other programming language it is desirable that
execution of a program does not terminate with some
kind of error. So, its of utmost importance to test all the
rules that have been written to perform as intended - the
interpreter could have other ideas.
Debugging a prolog program can be efficiently performed
by following every single step the interpreter is making.
This could be done by switching the tracing facility on
before execution initialises.
Every rule invoked will be displayed and every variable
bound to some value will be displayed too. If some rule
fails then an appropriate message will be issued and
backtracking will take place. Tracing can be switched
off at any time.
59. Graceful Termination
Some Prolog interpreters (e.g. LPA Prolog) also
allows the stepping through the execution with
watch windows to help the user through the
debugging session.
Now that the program is bug free we would like that
it terminates gracefully exectly when we have
finished with it, and not when the execution of
some rule results negatively. The programmer
must take into consideration all possible outcomes
and ensures beforehand that every situation has
been taken care of - positive or negative.
To do this, multiple versions of the same rule with
the same head but different terminating body have
to be written.
e.g. program8.pl
62. | ?- go.
# 0.11 seconds to consult file PROGRAM7.PL [C:PRO386W]
---------------
---------------
-- --
-- --
-- The --
--Program --
-- --
-- --
---------------
---------------
1. Student marks
2. Students in course
3. To EXIT
Select an option|: 5.
*****************
* WRONG INPUT *
*****************
Example - program8.pl
63. Select an option: 1.
Enter Student name: mark_morison.
Marks for student mark_morison
java --> 35
cobol --> 40
pascal --> 85
Select an option: 2.
Enter Course name: prolog.
bjork
mike_tyson
Select an option: 0.
*****************
* WRONG INPUT *
*****************
Select an option: 3.
Program terminating - thank you.
yes
| ?-
Example - program8.pl
64. 1. Consult and execute program8.pl to show that it works correctly.
2. Extend this program further by adding new menu options and corresponding
rules to do the following:
a. Publish the students’ names and respective marks for a particular topic
b. Publish the name and age of those students who got an “A”
c. Publish those students who are paying more than €300 for a course?
d. Publish those courses taken by students who were born in a specific year
e. Is it possible to find the total or average mark of a student?
f. Is it possible to work out how much fees a student owes?
3. Query and test the entire program again.
Submit code and screenshots of your testing in a file “Ans6.pdf”
Exercise 6
66. The great strength of Prolog is also its weakness in the case
of arithmetic.
Expressions ALWAYS evaluate to True or False, any other
action they perform, for instance producing an output, is
simply a side effect. Thus consider the expression:
in Pascal: X:=X+1;
or C: X=X+1;
This increments the value of the X variable by one by
assigning the new value to the variable. The problem
with some versions of Prolog is that there is no
assignment operator, in Prolog variables get a value
primarily by binding, not by assignment. Thus there is
no equivalent of “:=”, but we’re able to use the reserved
word “is” as already seen in program5.pl
Numeric Computing
67. Numeric Computing - “is”
When programming in Prolog, we are mainly concerned
with logical reasoning and symbolic computations. Still,
we sometimes need to do numerical computing. Prolog
enables us to evaluate arithmetic expressions by using
built-in procedure “is” which is an infix operator. For
example we can ask Prolog to compute the sum of two
numbers (program5.pl), such as:
? - X is 2+3.
and the answer will be
X=5
yes
The following dialogue with the Prolog interpreter illustrates
some Prolog features when doing arithmetic
computations:
68. Numeric Computing - “is”
?- X is 17div3.
X=5
yes
?- X is 2+3*(17div3). /*Integer division*/
X=13.4
yes
?- X is 17mod3. /*Remainder*/
X=2
yes
?- 5 is 2+3.
yes
?- 4 is 2+3.
no
?- 2+3 is 2+3.
no
69. Numeric Computing - “is”
The operator “is” evaluates the expression at the right-hand
side. If the left-hand side of the “is” operator is a
variable then its value is set to the computer value. If the
left-hand side is a number or a variable that already has a
value, the result is compared with this number. The
answer is ‘yes’ if both numbers are identical and ‘no’
otherwise.
e.g. program9.pl
rect(Length,Width) :-
Area is Length*Width,
Perimeter is (Length+Width)*2,
write(‘Area of rectangle is ’),
write(Area),nl,
write(‘Perimeter is ’),
write(Perimeter),nl.
70. Arithmetic Operators
Arithmetic expressions are Prolog structures that use arithmetic operators.
They are written in a special notation which improves readability. For
example, the expression 2 + 3 * ( 17 div 3 ) can be used in place of
the expression in the standard Prolog notation:
+ ( 2 , * ( 3 , div ( 17 , 3 ) ) )
The arithmetic expressions in the above example make use of functors like
‘+’, ‘-’, ‘*’, ‘/’, ‘mod’, ‘div’, each with two arguments. These functors
are built-in operators and are usually written in the infix notation (i.e.
between two arguments) in order to improve readability.
The usual order of precedence that defines the strength of the operator
binding is the same as for other programming languages.
71. Comparison Operators
There are also several built-in procedures for comparing
values of arithmetic expressions illustrated by the
following dialogue:
?- 17-1 > 5*3.
yes
?- 17+1 > 6*3.
no
?- 5>=5.
yes
?- 6+7 =:= 1+3*4.
yes
?- 7 =:= 5+3.
no
An overview of the built-in operators follows
72. Built-in Operators
Besides the operator “is” there are also several built-in
operators for comparing numbers.
X > Y the value of X is greater than Y
X < Y the value of X is less than Y
X >= Y X is greater than or equal to Y
X =< Y X is less than or equal to Y
X =:= YX andY are equal
X == YX is not equal to Y
X + Y sum of X and Y
X - Y difference of X and Y
X * Y product of X and Y
X / Y quotient of X and Y (real)
X div Y quotient of X and Y (integer)
X mod Y remainder of integer division
73. Example - program10.pl
These operators can be used to define Euclid’s algorithm for
finding the greatest common divisor of two numbers.
/* gcd(positive no , positive no , answer) */
gcd(X,Y,D) :- X=:=Y,
D is X.
gcd(X,Y,D) :- X<Y,
Y1 is Y-X,
gcd(X,Y1,D).
gcd(X,Y,D) :- X>Y,
X1 is X-Y,
gcd(X1,Y,D).
75. Example - program11.pl
find_total(M):- retract(totals(Marks,Num)),
Total is M+Marks,
No is Num+1,
assert(totals(Total,No)),
!,
fail.
option(1,Stud):- student(Sno,Stud,_,_),
option(_,Sno,Mark),
find_total(Mark).
option(1,Stud):- retract(totals(T,_)),
T >= 0,
write('Total Mark for student '),
write(Stud),
write(' is '),
write(T),nl,
continue.
76. Example - program11.pl
option(2,Stud):- student(Sno,Stud,_,_),
option(_,Sno,Mark),
find_total(Mark).
option(2,Stud):- retract(totals(T,N)),
T >= 0,
Aver is T/N,
write('Average Mark for student '),
write(Stud),
write(' is '),
write(Aver),nl,
continue.
option(3,Stud):- student(Sno,Stud,_,_),
option(Cno,Sno,Mark),
course(Cno,_,_,Cost),
find_total(Cost).
option(3,Stud):- retract(totals(T,N)),
T >= 0,
write('Total Cost for student '),
write(Stud),
write(' attending '),
write(N),
write(' course is '),
write(T),nl,
continue.
option(_,_):- nl,nl,write('*** NO OUTPUT ***'),nl,nl,
continue.
77. 1. Consult and execute program11.pl to show that it works correctly.
Note: Instead of consulting program7 you can copy and paste the contents directly.
2. Extend program3.pl to find out new facts and assert them as part of the working
memory by writing the following rules:
a. Aunt of a person using aunt(Aunt, NephewNiece).
b. Grandson of a person using grandson(Grandson, Grandperson).
c. Brothers of two male siblings brothers(Bro1, Bro2).
d. Sister of a person using sister(Sister, person).
e. Child of a person using child(Child, Parent).
3. Test the extended program to ensure new facts have been appended.
Note: Add the necessary parent_of/2 facts to ensure you test the new rules.
Submit code and screenshots of your testing in a file “Ans7.pdf”
Exercise 7
78. 8. Basic Lists
ICS1019 / ICS5003
• Introduction
• data types / data structures
• definition
• properties
• List Structure
• empty list
• single element manipulation
• bar notation [Head|Rest]
• List manipulation
• Head and Tail
• Recursion
• Pattern Matching
• Examples
79. So far in our discussion of Prolog we have hardly mentioned data types or data structures
at all. The reason for this omission is that data types are not extensively used in Prolog, the
majority of data is either string (symbol) or number and is represented in simple facts.
Prolog has no equivalent of the arrays, records, queues or other data storage devices of
procedural languages. Prolog makes use of one proper data structure, the LIST.
What is a List?
A list is:
• A data structure for representing sequences of elements.
• A sequence of of zero or more terms.
• A single term itself.
e.g. a list of vowels [a,e,i,o,u]
e.g. a list of fruit [pear,apple,orange,kiwi]
Lists - Introduction
80. The principal data structure used in Prolog is the list. Lists are not a data
structure provided as standard with many languages, but those languages that
are designed to work primarily with symbols use lists more than any other data
structure. This is because lists are highly flexible; the following are some of
their more useful properties:
Ä Lists are composed of elements that can be of any type; constants, variables,
structures or other lists. Any one list can contain different types of element,
although for most applications a list will usually contain elements of a constant
type.
Ä The elements of a list are held in an ordered sequence (unlike a set). This
ordering can be used to reflect some underlying organisation in the data being
stored. For example, lists are used to store trees, which are a hierarchical data
storage structure.
Lists - Properties
81. Ä A list can be of any size (it does not have to be declared to be a certain fixed size) and can
grow or shrink dynamically as required by a program during execution.
Ä Lists are especially suited to recursive access methods; one part of a list is operated on,
with the remainder being passed to a recursive call where one part of a list is operated on ...
etc.
Effective programming in Prolog relies heavily on the use of lists, and very
often specifically on the recursive processing of lists. Familiarity with these
structures and techniques is thus vital for you to succeed in better
understanding Prolog.
Further examples of lists:
» [] empty list
» [X] list with one element - variable X
» [1,2,3] list with three elements-values 1,2,3
» [a,[b,c]] two elements-’a’ and a list[b,c].
Lists - Examples
82. Lists - Components
A Prolog list can be created very easily. A list is simply a comma seperated
sequence of values enclosed in square brackets.
The empty list, [] is a particularly useful object because of its unique
characteristics that can be used in a number of roles. Any list which is not
empty has one or more elements - basically all the remaining lists.
So, a list is either empty, or it is composite, having a head and a tail.
The HEAD of a non-empty list is its first element, while the rest of the
elements (a sub list) is the TAIL -the whole list minus the first.
[1,2,3] Head is ‘1’ Tail is [2,3]
[a,b] Head is ‘a’ Tail is [b]
[one] Head is ‘one’ Tail is [ ]
[[1,2],3] Head is [1,2] Tail is [3]
[a,[b,c],d,e] Head is ‘a’ Tail is [[b,c],d,e]
83. To access the components of lists, we employ a special notation which
makes the head and the tail explicit. Apart from the square brackets
Prolog provides one operator specifically for use in list handling, but it
is a very useful one. The vertical bar, “|”, is used to divide a list into
the two parts, head & tail.
This may not seem to be very useful at first, but this operator allows us
to sequentially split each of the elements off a list, one by one in order
from the front of the list, to perform some operation with them. This
process of sequential extraction is almost always performed
recursively; we split the head off the list and pass the tail in a recursive
call where we split the head off the list ... etc.
All along, pattern matching is still occuring.
Lists - Bar Notation
84. As mentioned before lists can contain numbers, symbols, complete clauses or other lists. If we want
to bind a particular list value we can either simple pattern match the list, or use the “|” operator to
extract an atom from a particular list position.
list head tail bar notation
[a] a [] [a | [] ]
[a,b] a [b] [a | [b] ]
[a,b,c] a [b,c] [a | [b,c] ]
Variables List Bar notation Binds
[X|Y] [a,b,c] [a|[b,c]] X=a, Y=[b,c]
[X|Y] [a] [a|[]] X=a, Y=[]
[X|Y] [f,[s,t]] [f|[s,t]] X=f,Y=[[s,t]]
[X|Y] [[f,s],t] [[f,s]|[t]] X=[f,s],Y=[t]
[[f,X]|Y] [[f,s],t] [[f,s]|[t]] X=s, Y=[t]
[X|Y] [] fails
Lists - Pattern matching
86. Now we have seen how to pattern match particular elements in a list, we can move
on to techniques for iteratively manipulating lists. The majority of lists have
elements of the same type that often need to be assessed in an iterative, element-by-
element way. Any such iterative process will have to be programmed recursively in
Prolog as this is the only appropriate mechanism available.
To start with a simple example, the print_list rule defined in program13.pl
Example - program13.pl
print_list(List):- consult(prog12),
List(X),
printout(X).
printout([ ]).
printout([H|T]):- write(H),nl,
printout(T).
Lists - Recursion
87. The main clause uses two variables (that are often named H and T by convention) which
bind to the head and tail of the input list. The head is then printed out using the write
predicate (standard built-in, just like consult) and the rule is then called recursively with
just the tail as argument. At each stage the stopping clause is tested, but only
succeeds when the tail is the empty list (when all the elements have been processed). At
this point the recursion is halted by the stopping rule.
Other examples:
print_list([hello, how, are, you]).
hello
how
are
you
Lists - Stopping Clause
88. find_total(0,[]).
find_total(Total,[H|T]):- find_total(SubT,T),
Total is H + SubT.
find_average(0,[]).
find_average(Average,List):- find_total(Total,List),
find_number(Number,List),
Average is Total/Number.
find_number(0,[]).
find_number(N,[ _ | T ]):- find_number(Nn,T),
N is Nn + 1.
find_max(X,[X]).
find_max(H,[H|T]):- find_max(Max,T),
H > Max.
find_max(Max,[H|T]):- find_max(Max,T),
H =< Max.
Lists - program14.pl
89. | ?- find_total(T,[3,5,7,5]).
T = 20 ;
| ?- find_total(T,[ ]).
T = 0 ;
| ?- find_total(T,[7]).
T = 7 ;
| ?- find_average(A,[3,5,7,5]).
A = 5 ;
| ?- find_average(A,[3]).
A = 3 ;
| ?- find_average(A,[ ]).
A = 0 ;
| ?- find_max(A,[3,5,7,5]).
A = 7 ;
| ?- find_max(A,[3]).
A = 3 ;
| ?- find_max(A,[ ]).
no
Lists - program14.pl
90. 1. Consult and execute program14.pl to show that it works correctly by issuing
the following queries:
a. find_total(T, ["a","b","c"]). What is happening?
b. find_max(T,[]). How can this be addressed?
c. find_average(A,[1,"b",3]). Does this make sense?
2. Write a recursive rules to evaluate the product of a list of positive Integers.
prod(List of Integers, Product).
e.g. prod([12, 18, 6], P) binds P to 1296.
3. Write a recursive rules to find the minimum elelent within a list of Integers.
min(List of Integers, Minimum).
e.g. min([4,0,-1,23,7], M) binds M to -1
Submit code and replies in a file “Ans8.pl”
Exercise 8
91. 9. Advanced Lists
ICS1019 / ICS5003
• Complex List manipulation
• Pattern Matching
• Recursive List handling
• Provided operations
• append
• delete
• last
• member
• sum_list
• Examples
• Tutorial
• questions and queries
• handout
92. Lists are used very widely in Prolog, and there are some specific list
relations that are worth getting to know and understand very well. If
nothing else, defining them is something that often comes up in Prolog
exams!? More importantly though, understanding them will make it easier
to write new list relations, because the recursive way the standard ones
work is also used very widely. Pattern matching is still occurring all
throughout the list handling and recursion.
Because we can only get access at first to the head of the list - the
first element, we almost always write recursive relations to process the
remaining elements in the list held in the tail. A stooping clause needs
always to be written in order to terminate the recursion, otherwise you get
into a continuous loop.
Advanced Lists - Introduction
93. Considering checking for membership of an element within a list. That is, we want to
define a rule called is_a_member_of(X,L) that succeeds if the element ‘X’ is present
within the list ‘L’. The way to code such recursive rules is to state out the basic facts in
English, as simple as possible.
1. If the element X is actually the head element of the list, then we’re sorted.
So: is_a_member_of(X,[X| _ ]). succeeds
2. If X is not the head, then X might be present within the tail. We accomplish this
by chopping the list head off and call the same rule using the tail only.
So: is_a_member_of(X,[_|Tail]):- is_a_member_of(X,Tail).
3. Finally, graceful termination. No element is a member of an empty set.
(could do without it … why?)
So: is_a_member_of(_,[]):- write('Not a member’), nl.
Advanced Lists - Membership program15.pl
94. Another widely used procedure is to concatenate or append one or more elements
(a list basically), to another list to form a longer list.
e.g. | ? - append_to([a,b,c],[d,e,f],Whole).
Whole = [a,b,c,d,e,f].
Reasoning this out:
1. If you append an empty list to anything, you still have the same ‘anything’.
So: append_to([ ],X,X). simple!!???!!
2. If the list being appended is not empty, then we’ll chop its head off and
append it to the second list, while we call the same function again to append
the remaining part of the first list (the tail) the newly formed list.
So: append_to([A|B],Second,[A|C]):- append_to(B,Second,C).
Advanced Lists - Concatenation program15.pl
95. Deleting a particular element from a list seems simple, but what if more than one
occurence of the same element are present within the list. Two seperate rules, single_del
and multiple_del will perform these operations.
single_del(X,[X|Final],Final).
single_del(X,[_|Tail],Result):- single_del(X,Tail,Result).
e.g. | ? - single_del(2,[1,2,3,2,5],R). binds R = [1,3,2,5]
multiple_del(_,[],[]).
multiple_del(X,[X|Tail],Final):-
multiple_del(X,Tail,Final).
multiple_del(X,[H|Tail],[H|Result]):-
multiple_del(X,Tail,Result).
e.g. | ? - multiple_del(2,[1,2,3,2,5],R). binds R = [1,3,5]
Advanced Lists - Deleting program15.pl
96. It is sometimes necessary to reverse the elements within in a list. This kind of procedure has
to be written recursively because we do not know beforehand how many elements are within
the list.
reverse_it([ ],[ ]).
reverse_it([A|B],C):- reverse_it(B,D),
append_to(D,[A],C).
e.g. | ? - reverse_it([1,2,3,4,5],Rev). binds Rev = [5,4,3,,2,1]
e.g. | ?- reverse_it([1,[a,b,c],2,3],R). binds R = [3,2,[a,b,c],1]
e.g. | ?- reverse_it([1],R). binds R = [1]
e.g. | ?- reverse_it([ ],R). binds R = [ ]
Advanced Lists - Reversing program15.pl
97. To determine the biggest or smallest valued item within a list we will have to go through
the list till the last item, assume that it is the value we are looking for and backtrack back
to the first item checking the value of the items each time.
biggest([X],X).
biggest([H|T],H):- biggest(T,Tbig),
H >= Tbig.
biggest([H|T],Tbig):- biggest(T,Tbig),
H < Tbig.
e.g. | ?- biggest([2,4,1,3,5,2],Big). binds Big = 5
| ?- biggest([2],Big). binds Big = 2
| ?- biggest([],Big). returns no
Advanced Lists - MinMax program15.pl
98. The final example of provided procedures is the rule sublist_of(SubList,List) which
succeeds if the ‘SubList’ is contained within the ‘List’. That is, all the elements within the
SubList appear consecutively and without interruption in List, not necessarily at the begining
or at the end.
sublist_of(A,B):- append_to(_,A,C),
append_to(C,_,B).
e.g. | ?- sublist_of([1,2,3],[1,2,4,1,2,3,5]).
yes
| ?- sublist_of([],[1,2,3]).
yes
| ?- sublist_of([1],[1]).
yes
Not a particular efficient rule as the “_” being appended can overflow the memory heap.
Advanced Lists - Sublist program15.pl
99. All examples in lecture 9 are in program15.pl
Write a rule to print out the nth. element within a given list.
/*n_th_element(item_number,list,the_item */
n_th_element(1,[H| _ ],H).
n_th_element(N,[ _ |Tail],Item):- NewN is N -1,
n_th_element(NewN,Tail,Item).
| ?- n_th_element(3,[a,b,c,d,e],H). binds H = c
| ?- n_th_element(4,[a,b,c,d,e],d). returns yes
| ?- n_th_element(0,[a,b,c,d,e],H). returns no
| ?- n_th_element(6,[a,b,c,d,e],H). returns no
| ?- n_th_element(3,[],H). returns no
Advanced Lists - Final list example
100. After going through a number of list examples and different list manipulations
it can be noticed that repeatedly we were employing:
1. Pattern matching
2. Backtracking
3. Recursion
We will be covering recursion further without the use of lists in the next
lecture.
Lists are very common data structures in non-numeric programming. They
are extensively used in Prolog especially to represent parse trees, grammars,
city maps, computer programs, and mathematical entities such as graphs,
formulae, & functions.
Finally, there is another language called LISP (LISt Programming) whose
only data structures available are the constant and the list.
Advanced Lists - Conclusion
101. Write clauses for the following list handling rules:
1. bubble(N, List, NewList) which binds NewList with the List having its Nth
element brought to the front.
e.g. bubble(3,[a, b, c, d, e],Ans) binds Ans to [c, a, b, d, e]
2. swap(X, Y, List, NList) which binds NList with List but with its Xth and Yth
elements swapped.
e.g. swap(2, 4, [a, b, c, d, e], Final) binds Final to [a, d, c, b, e]
Submit your code in a file “Ans9.pl”
Exercise 9
102. 10. Recursion
ICS1019 / ICS5003
• Definition
• Mechanism of recursion
• starting recursion
• stopping condition
• more than one clause
• simple example
• Recursive Mathematical Operations
• factorial of an integer
• fibbonacci of an integer
• greatest common divisor of an integer
• Examples
103. The Oxford English Dictionary defines the word “recursion” in the
following way:
RECURSION. [Now rare or obs.1626].
A backward movement, return.
This definition is cryptic and perhaps outdated. Recursion is now a
very popular and powerful technique in the world of non-numerical
programming. It is used in two ways. It can be used to describe
structures that have other structures as components. It can also be
used to describe programs that need to satisfy a copy of themselves
before they themselves can succeed. Sometimes, beginners view
recursion with some suspicion, because, how is it possible for some
relationship to be defined in terms of itself? In Prolog, recursion is
the normal way of viewing data structures and programs.
Recursion - Introduction
104. We have already encountered recursion in several examples when dealing
with lists (look at program15.pl). We have also pointed out earlier that Prolog
has no iterative control structures like the FOR, WHILE, or DO loops of
other languages. This is initially surprising, but is less so when you realise
that Prolog has very few built-in predicates at all. Prolog allows iterative
action through the construction of recursive rules (rules which call
themselves), and no built-in predicate is required for this.
Any recursive operation in Prolog must be defined as part of a particular rule.
A recursive rule is one that calls itself at some point in the body of the rule.
When writing recursive operations, a programmer has to abide with specific
requirements.
Recursion in Prolog
105. In general there are two functions which need to be performed in any recursive
Prolog rule.
The first is to make recursion start, and the second is to make it stop once
started. Starting the recursive process involves a rule clause that calls its own
head, stopping it requires a rule clause that defines a condition to prevent
further backtracking in the rule.
These two required mechanisms mean that a recursive rule must always
have at least two clauses, one to perform each function. We have seen this
two-clause structure when performing list manipulation in program15.pl. A
recursive rule can have more than two clauses if this is required, but two is the
absolute minimum.
Recursion - Properties
106. Probably the most common example of a recursive operation in computer
science is the calculation of the factorial of an integer. This is often used
because the factorial operation is relatively simple and is not included as a
built-in function in most languages. The factorial is the product of an integer
and every integer less than it. Thus the factorial of 4 is 4! = 4 x 3 x 2 x 1 = 24.
To implement this operation we require two functions. The stopping
condition is also called the base step and can be defined in terms of the
simplest factorial, i.e. 1! = 1
So, factorial(1,1). /* factorial(num,ans) */
Now, the recursive call:
factorial(N,Ans):- F is N - 1,
factorial(F,FactF),
program16.pl Ans is N * FactF.
Recursion - Simple example
107. To proceed any further we need to understand the mechanism of Prolog
recursion. At each recursive call the variables in the current iteration are
saved (on stack) as the call is made. Thus each stage in the process of the
factorial example has its own F and FactF variables saved. When we get
to the stopping clause these variables are then used one-by-one in reverse
order to work out the FactF value to be returned. The last line of the
recursive clause performs this calculation on the stored variables.
In effect this recursive rule counts down through the values below the
input N, saves each in a different F variable, then once halted (N = 1), it
“unwinds” the process and multiplies each FactF by its particular F value
to return the “Ans” value.
Recursion - Mechanism
108. This unwinding process is not very evident from the code, but it is
implicit in the last line. If any clause exists after the recursive call
then the clause variables are saved for use in the “unwinding”
process when they may be used.
If the recursive call in the recursive clause is not the last line of that
clause then any lines following after the recursive call are not
executed whilst the recursive calls are being made. The recursion
must proceed to its end (as defined by the stopping clause) before
these lines can be executed. Since these later clauses may well use
the variables from the previous clauses these variables must be
saved. In effect each set of variables from each loop in the recursive
process must be saved for later use.
Recursion - Mechanism
109. This appears odd at first because the variables in each recursive
loop will have the same names. This in out factorial example we
will end up saving the F and FactF variables for every iteration of
the recursion. This odd state of affairs, with duplicate variables
saved at every iteration, is resolved by the Prolog interpreter
creating a series of what are called stack frames. A frame in this
sense is a copy of all the variables local to the rule, and at each
recursive step the current set of variables are pushed on to the stack.
When the recursion is complete and needs to “unwind” the stack
frames are popped off the stack one by one to be dealt with by the
remaining rule clauses in turn.
Recursion - Mechanism
110. Another standard example of a recursive operation used in computer science is
the calculation of the fibbonacci series. Each number in this series is the sum of
the fibbonacci value of the two previous numbers. So, fib(N) = fib(N-1) +
fib(N-2).
Unlike the factorial example the fibbonacci series is actually defined in maths
recursively, and is only given an actual value on the basis of its stopping
conditions where the value of the initial two values, F(1) and F(2) are both
defined to be 1. All other fibbonacci values are built up on the basis of these
two values.
The full fibbonacci series is as follows:
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377,...
The calculation of this series is ideally suited to a recursive technique due to the
definition of the operation in terms of itself.
Recursion - fibbonacci program15.pl
111. /* fib(term number, term value) */
/* e.g. 6th. term is 8 is equal to fib(6,8) */
fib(1,1).
fib(2,1).
fib(N, Result) :- N1 is N - 1,
N2 is N - 2,
fib(N1, Result1),
fib(N2, Result2),
Result is Result1 + Result2.
This rule is built on three seperate clauses because we require two stopping clauses since
the fibbonacci of both the 1st. and 2nd. terms are defined to be 1.
The most important part of the rule is the last line of the third clause which is evaluated
after that the previous two recursive calls have been satisfied - “unwinding”
Recursion - fibbonacci program15.pl
112. Recursion - gcd program10.pl
The greatest common dvisor using Euclid’s algorithm was introduced in Lecture 7 when
mathematical operators were introduced. It is worth having a look at this program again
to appreciate the recursive calls done to work out the GCD of two numbers.
/* gcd(positive no , positive no , answer) */
gcd(X,Y,D) :- X=:=Y,
D is X.
gcd(X,Y,D) :- X<Y,
Y1 is Y-X,
gcd(X,Y1,D).
gcd(X,Y,D) :- X>Y,
X1 is X-Y,
gcd(X1,Y,D).
113. Recursion is a very important method in Prolog because of the lack of iterative
capabilities inherent in Prolog itself.
Writing recursive rules is not an easy task and is frequently at the center of a
number of questions in an exam.
To effectively and efficiently write recursive clauses it is best to:
1. Understand the task to be done exactly.
2. Figure out what the stopping condition is and how best to represent it.
3. Write the starting clause of the recursion.
Pay attention to:
• Place the stopping condition before the other clauses in order to check it out
every time a recursive call is performed
• Place the recursive call in the correct position within the clause itself.
Recursion - Conclusion
114. Write recursive clauses to perform these stack operations:
1. push(Item, Old Stack, NewStack) which binds NewStack to the OldStack with the Item at the
head
e.g. push(x, [a, b, c], NewStack) binds NewStack to [x, a, b, c].
2. pop(Item, Old Stack, NewStack) which binds Item to the head of the OldStack while NewStack is
the OldStack without its head
e.g. pop(Item, [a, b, c], NewStack) binds Item to ‘a’ and NewStack to [b, c].
3. reverse(OldStack, NewStack) which binds NewStack to the reversed list of items within the
OldStack
e.g. reverse([a, b, c], NewStack) binds NewStack to [c, b, a].
4. empty(OldStack, NewStack) which binds NewStack to a stack containing on the top of the
OldStack
e.g. empty([3,5,6,2,7], Afresh) binds Afresh to [3].
5. display_bottom(Stack) which displays the last item within the Stack.
e.g. display_bottom([f, i, l, o]) displays o
Submit your code in a file “Ans10.pl”
Exercise 10
115. 11. Advanced Recursion with Lists
ICS1019 / ICS5003
• Writing recursive rules for lists
• Pay attention to detail
• Empty list
• Stopping condition
• Extremes
• Order of clauses
• Winding
• Not losing the Head
• Unwinding
• Recovering list parts
116. Write recursive rules to manipulate the following lists:
Exclude the items of one list from another list.
e.g. exclude([1,2,3], [2,3,4], X) binds X with [4] while
exclude([a,b,c],[1,2,3],Y) binds Y with [1,2,3]
Identify and Remove an item from a list.
e.g. idRem(3,[a,b,c,d,e], FinalList, Item) would remove the 3rd item from
the list to bind FinalList with [a,b,d,e] and Item with c
Differentiate between two list.
e.g. diff([a,b,c,d,e], [a,b,d,e],DiffList) would bind DiffList with [c] while
diff([1,2,3],[a,b,c],DL) would bind DL with [1,2,3]
Subdivide a list into two sublist from a specified position.
e.g. subD([a,b,c,d,e],2,FL,SL) binds FL to [a,b] and SL to [c,d,e] while
subD([p,q,r],3,A,B) binds A to [p,q,r] and B to []
Drown a specified element from a list to the end of the list.
e.g. drown(4,[a,b,c,d,e],Ans) binds Ans to [a,b,c,e,d] while
drown(2,[p,q,r],FinalL) binds FinalL to [p,r,q]
11. Advanced Recursion with Lists
117. Sets can be represented in Prolog using Lists. Write the following sets handling rules:
member_of(Element,Set) which returns TRUE if Element is an element within the list Set.
E.g. member_of(3, [1,2,3,4]) returns TRUE.
intersection(SetA, SetB, Int) which binds Int to the list containing those elements that are in list SetA as well as in list SetB.
E.g. intersection([1,2,3], [2,3,4], Ans) binds Ans to [2,3].
union(SetA, SetB, Uni) which binds Uni to the list containing those elements that are either in sets A or B or in both.
E.g. union([1,2,3], [2,3,4], Ans) binds Ans to [1,2,3,4].
xor(SetA, SetB, XOR) which binds XOR to the list containing those elements that are in list SetA, or in list SetB, but not in both.
E.g. xor([1,2,3], [2,3,4], Ans) binds Ans to [1, 4].
complement(SetA, SetB, Comp) which binds Comp to the list containing those elements that are not in list SetA but in SetB only.
E.g. complement([1,2,3], [2,3,4], Ans) binds Ans to [4].
11. More Advanced Recursion with Lists
118. A Stack is data structure capable of storing data in a particular way. Items are pushed on top of each other in a sequential way and
retrieved by accessing the item at the top. In this way the First item In is the Last one Out (FILO) and the Last one In will be the First
one Out (LIFO). You are asked to write three clauses to manage and display the data within a stack, which in this case will be
represented in form of a list. The required predicates are:
push(Item, OldStack, NewStack) which binds NewStack to OldStack with the Item pushed on top
e.g. push(a, [b,c], NewStack) binds NewStack to [a,b,c].
pull(Item, OldStack, NewStack) which binds NewStack to OldStack with the head removed from the top and bound to Item
e.g. pull(Item, [3,5,6,2,7], Fresh) binds Item to 3 and Fresh to [5,6,2,7].
display_Stack(Stack) which displays the contents of the Stack starting from the top.
e.g. display_stack([b,w,a,r,f]) displays bwarf
empty_Stack(Stack) which return TRUE if the Stack is empty, otherwise return FALSE.
e.g. empth_stack([w,a,r]) return FALSE
11. Another Advanced Recursion with Lists
119. Write clauses for the following list handling rules:
1. bubble(N, List, NewList) which binds NewList with the List having its
Nth element brought to the front.
e.g. bubble(3,[a, b, c, d, e],Ans) binds Ans to [c, a, b, d, e]
2. swap(X, Y, List, NList) which binds NList with List but with its Xth and Yth
elements swapped.
e.g. swap(2, 4, [a, b, c, d, e], Final) binds Final to [a, d, c, b, e]
3. occur(Element,List,Num) which binds Num with the number of occurrences
that the item Element happens to be in the List.
e.g. occur(b, [a,b,c,a,b,d,e], Repeat) binds Repeat to 2
Submit your code in a file “Ans11.pl”
Exercise11
120. 12. CFG - Context Free Grammar
ICS1019 / ICS5003
• Definition
• Simple example
• CFG Prolog syntax
• Using Append
• Using Difference Lists
• Adding Recursion
• Avoiding left recursion
• DCG – Definite Clause Grammar
• Simple formal language
121. What is a CFG?
o A way to deal with grammars in a formal
way;
o It’s what computational linguists do;
o A finite collection of rules which tell
us that a certain sentences is
grammatically correct or valid;
o Important to distinguish between a
natural language and formal
language.
o Formal language is simply a set of
strings that have no real meaning.
12. CFG - Context Free Grammar
122. Consider this small English fragment that
contains 9 context free rules…
s -> np vp
np -> det n
vp -> v np
vp -> v
det -> a
det -> the
n -> woman
n -> man
v -> shoots
12. CFG – Simple example
Rules
Consider the string of words
“a woman shoots a man”
This is it’s Parse Tree.
‘defined by’
‘consists of’
‘built by’
Rules
Non-terminal symbols
Terminal symbols
123. Turn the rules into Prolog…
s(S):- np(X), vp(Y), app(X,Y,S).
np(NP):- det(X), n(Y), app(X,Y,NP).
vp(VP):- v(X), np(Y), app(X,Y,VP).
vp(VP):- v(VP).
det([a]).
det([the]).
n([woman]).
n([man]).
v([shoots]).
------------------------------------------------------------
?-s([a, woman, shoots, a, man]).
?-s(AllPossibleSentences).
12. CFG – Using Append
124. o The key idea underlying
difference lists is to
represent the information
about grammatical
categories not as a single
list, but as the difference
between two lists.
o E.g.
[a,woman,shoots,a,man] [].
12. CFG – Using Difference Lists
What about ...
s(X,Z):- np(X,Y), vp(Y,Z).
np(X,Z):- det(X,Y), n(Y,Z).
vp(X,Z):- v(X,Y), np(Y,Z).
vp(X,Z):- v(X,Z).
det([the|W],W).
det([a|W],W).
n([woman|W],W).
n([man|W],W).
v([shoots|W],W).
125. Consider using ‘and’, ‘or’, and ‘but’ as part of our
language, then we can have composite sentences.
s -> s conj s
conj -> and
conj -> or
conj -> but
And in Prolog …
s(A,B):- s(A, C),
conj(C, D),
s(D, B).
s(A,B):- np(A, C),
vp(C, B).
12. CFG – Adding Recursion
126. s --> simple_s.
s --> simple_s, conj, s.
simple_s --> np,vp.
np --> det,n.
vp --> v,np.
vp --> v.
det --> [the].
det --> [a].
n --> [woman].
n --> [man].
v --> [shoots].
conj --> [and].
conj --> [or].
conj --> [but].
12. CFG – Avoiding Left Recursion
And in Prolog ...
s(A,B):- simple(A,B).
s(A,B):- simple(A,X), conj(X,Y), s(Y,B).
simple(X,Z):- np(X,Y), vp(Y,Z).
np(X,Z):- det(X,Y), n(Y,Z).
vp(X,Z):- v(X,Y), np(Y,Z).
vp(X,Z):- v(X,Z).
det([the|W],W).
det([a|W],W).
n([woman|W],W).
n([man|W],W).
v([shoots|W],W).
conj([and|W],W).
conj([or|W],W).
conj([but|W],W).
127. o A DCG is a nice notation for writing
grammars that hides the underlying
difference list variables.
s -> np,vp.
np -> det,n.
vp -> v,np.
vp -> v.
det -> [the].
det -> [a].
n -> [woman].
n -> [man].
v -> [shoots].
12. DCG – Definite Clause Grammar
128. Consider anbn ... What is this language?
e.g.s. ab, aabb, aaabbb, aaaaaabbbbbb, ϵ .
Another e.g. Consider the formal language anb2n
12. DCG – for a simple formal lang.
CFG
s -> ϵ
s -> l s r
l -> a
r -> b
DCG
s -> [].
s -> l,s,r.
l -> [a].
r -> [b].
Prolog
s([], []).
s(A,B):-ss(A,B).
ss(A,B):- l(A,P),
r(P,B).
ss(A,B):- l(A,P),
s(P,Q),
r(Q,B).
l([a|X],X).
r([b|X],X).
129. Consider the DCG below:
1. Implement the above DCG in Prolog using Difference Lists.
2. Write a query to generate all possible sentences.
3. Is the DCG syntactically correct?
4. Write queries to check whether the following sentences are part of
this grammar, and state whether they are or not.
a. "the waiter brought the meal to the table";
b. "a meal brought by the waiter";
c. "the waiter brought the meal of the day’’.
5. Draw the parse trees for the above sentences.
Submit your code and replies in a file “Ans12.pl”
Exercise 12
130. Thank you for your attention.
If you have any questions, now is the right time to ask!
Contact me on the details below for further info:
Prof Matthew Montebello
https://www.um.edu.mt/profile/matthewmontebello matthew.montebello@um.edu.mt
(+356) 2340 2132