0% found this document useful (0 votes)
307 views31 pages

Prolog Notes-Complete

Prolog is a logic programming language suited for symbolic and non-numeric computation, like in artificial intelligence. A Prolog program consists of rules and facts. Facts are simple statements that are true, like "john_is_cold." and can take arguments, like "eats(fred, oranges)." Rules allow conditional statements, like "mortal(X) :- human(X)." meaning X is mortal if X is human. Variables in Prolog start with a capital letter and allow general queries, like "eats(fred, What)." to find What fred eats by unifying What with the object like "mangoes."

Uploaded by

Salman Ijaz
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
307 views31 pages

Prolog Notes-Complete

Prolog is a logic programming language suited for symbolic and non-numeric computation, like in artificial intelligence. A Prolog program consists of rules and facts. Facts are simple statements that are true, like "john_is_cold." and can take arguments, like "eats(fred, oranges)." Rules allow conditional statements, like "mortal(X) :- human(X)." meaning X is mortal if X is human. Variables in Prolog start with a capital letter and allow general queries, like "eats(fred, What)." to find What fred eats by unifying What with the object like "mangoes."

Uploaded by

Salman Ijaz
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 31

4.

3 Further Programming
4.3.1 Programming Paradigms

1
Introduction to Prolog(Programming in Logic)
Prolog is a logic language that is particularly suited to programs that involve symbolic or non-
numeric computation. For this reason it is a frequently used language in Artificial Intelligence
where manipulation of symbols and inference about them is a common task.

Prolog consists of a series of rules and facts. A program is run by presenting some query and
seeing if this can be proved against these known rules and facts.

1. Simple Facts
In Prolog we can make some statements by using facts. Facts either consist of a particular item
or a relation between items. For example we can represent the fact that it is sunny by writing
the program:
sunny.

We can now ask a query of Prolog by asking


?- sunny.

?- is the Prolog prompt. To this query, Prolog will answer yes. sunny is true because (from
above) Prolog matches it in its database of facts.

Facts have some simple rules of syntax. Facts should always begin with a lowercase letter and
end with a full stop. The facts themselves can consist of any letter or number combination, as
well as the underscore _ character. However, names containing the characters -,+,*,/, or other
mathematical operators should be avoided.

Examples of Simple Facts


Here are some simple facts about an imaginary world. /* and */ are comment delimiters

john_is_cold. /* john is cold */

raining. /* it is raining */

john_Forgot_His_Raincoat. /* john forgot his raincoat */

fred_lost_his_car_keys. /* fred lost his car keys */

peter_footballer. /* peter plays football */

2
These describe a particular set of circumstances for some character john. We can interrogate
this database of facts, by again posing a query. For example: {note the responses of the Prolog
interpreter are shown in italics}

?- john_Forgot_His_Raincoat.

yes

?- raining.

yes

?- foggy.

no

The first two queries succeed since they can be matched against facts in the database above.
However, foggy fails (since it cannot be matches) and Prolog answers no since we have not told
it this fact.

2. Facts with Arguments


More complicated facts consist of a relation and the items that this refers to. These items are
called arguments. Facts can have arbitrary number of arguments from zero upwards. A general
model is shown below:
relation(<argument1>,<argument2>,....,<argumentN> ).

Relation names must begin with a lowercase letter


likes(john,mary).

The above fact says that a relationship likes links john and mary. This fact may be read as either
john likes mary or mary likes john. This reversibility can be very useful to the programmer,
however it can also lead to some pitfalls. Thus you should always be clear and consistent how
you intend to interpret the relation and when.

Finally, remember names of the relations are defined by you. With the exception of a few
relations that the system has built into it, the system only knows about relations that you tell it
about.

3
Facts with Arguments Examples 1
An example database. It details who eats what in some world model.

eats(fred,oranges). /* "Fred eats oranges" */

eats(fred,t_bone_steaks). /* "Fred eats T-bone steaks" */

eats(tony,apples). /* "Tony eats apples" */

eats(john,apples). /* "John eats apples" */

eats(john,grapefruit). /* "John eats grapefruit" */

If we now ask some queries we would get the following interaction:


?- eats(fred,oranges). /* does this match anything in the database? */

yes /* yes, matches the first clause in the database */

?- eats(john,apples). /* do we have a fact that says john eats apples? */

yes /* yes we do, clause 4 of our eats database */

?- eats(mike,apples). /* how about this query, does mike eat apples */

no /* not according to the above database. */

?- eats(fred,apples). /* does fred eat apples */

no /* again no, we don't know whether fred eats apples */

Facts with Arguments Examples 2


Let us consider another database. This time we will use the predicate age to indicate the ages
of various individuals.

age(john,32). /* John is 32 years old */

age(agnes,41). /* Agnes is 41 */

age(george,72). /* George is 72 */

age(ian,2). /* Ian is 2 */

age(thomas,25). /* Thomas is 25 */

4
If we now ask some queries we would get the following interaction:

?- age(ian,2). /* is Ian 2 years old? */

yes /* yes, matches against the fourth clause of age */

?- agnes(41). /* for some relation agnes are they 41 */

no /* No. In the database above we only know about the relation


*/

/* age, not about the relation agnes, so the query fails */

?- age(ian,two) /* is Ian two years old? */

no /* No. two and 2 are not the same and therefore don't match */

3. Variables and Unification


Prolog's pattern matching is called unification . In the case where the database contains only
facts, unification succeeds if the following three conditions hold.

 The predicate named in the goal and databases are the same.
 Both predicates have the same arity.
 All of the arguments are the same.

How do we say something like "What does Fred eat"? Suppose we had the following fact in our
database:
eats(fred,mangoes).

How do we ask what fred eats. We could type in something like


?- eats(fred,what).

However Prolog will say no. The reason for this is that what does not match with mangoes. In
order to match arguments in this way we must use a Variable. The process of matching items
with variables is known as unification. Variables are distinguished by starting with a capital
letter. Here are some examples:
X /* a capital letter */

VaRiAbLe /* a word - it be made up or either case of letters */

My_name /* we can link words together via '_' (underscore) */

5
Thus returning to our first question we can find out what fred eats by typing

?- eats(fred,What).

What=mangoes

As a result of this query, the variable What has matched (or unified) with mangoes. We say that
the variable What now has the binding mangoes. When we pose a query, if the query is
successful, Prolog prints both the variable and the variable name, as we see above.

Variable Examples 1
Let's consider some examples using facts. First consider the following database.
loves(john,mary).

loves(fred,hobbies).

Now let's look at some simple queries using variables


?- loves(john,Who). /* Who does john love? */

Who=mary /* yes , Who gets bound to mary */

yes /* and the query succeeds*/

?- loves(arnold,Who) /* does arnold love anybody */

no /* no, arnold doesn't match john or fred */

?- loves(fred,Who). /* Who does fred love */

Who = hobbies /* Note the to Prolog Who is just the name of a variable, it */

yes /* semantic connotations are not picked up, hence Who unifies */

/* with hobbies */

Variable Examples 2
Here are some more difficult object/variable comparisons. Consider the following database
showing a library of cassette tapes. Notice the final argument to tape, which is the name of a
favourite song from the album.

tape(1,van_morrison,astral_weeks,madam_george).

tape(2,beatles,sgt_pepper,a_day_in_the_life).

6
tape(3,beatles,abbey_road,something).

tape(4,rolling_stones,sticky_fingers,brown_sugar).

tape(5,eagles,hotel_california,new_kid_in_town).

Let's now look at some queries.

?- tape(5,Artist,Album,Fave_Song). /* what are the contents of tape 5 */

Artist=eagles

Album=hotel_california

Fave_Song=new_kid_in_town

yes

?- tape(4,rolling_stones,sticky_fingers,Song). /* find just song */

Song=brown_sugar /* which you like best from the album */

yes

4. Rules
So far we have looked at how to represent facts and to query them. Now we move on to rules.
Rules allow us to make conditional statements about our world. Each rule can have several
variations, called clauses. These clauses give us different choices about how to perform
inference about our world. Let's take an example to make things clearer.

Consider the following

Rule 1

'All men are mortal':

We can express this as the following Prolog rule


mortal(X) :-

human(X).

The clause can be read in two ways (called either a declarative or a procedural interpretation).
The declarative interpretation is "For a given X, X is mortal if X is human." The procedural
interpretation is "To prove the main goal that X is mortal, prove the subgoal that X is human."

7
8
Rule 2
To continue our previous example, lets us define the fact 'Socrates is human' so that our
program now looks as follows:

mortal(X) :-

human(X).

human(socrates).

If we now pose the question to Prolog

?- mortal(socrates).

The Prolog interpreter would respond as follows:

yes

Why was this? Well in order to solve the query ?- mortal(socrates)., we used the rule we saw
previously. This said that in order to prove someone mortal, we had to prove them to be human.
Thus from the goal Prolog generates the subgoal of showing human(socrates).

In the above example we were able to match human(socrates) against the database described at
the top of this card. In Prolog we say that the subgoal succeeded, and as a result the overall goal
succeeded. We know when this happens because Prolog prints yes.

Rule 3
We can also use variables within queries. For example, we might wish to see if there is
somebody who is mortal. This is done by the following line.

?- mortal(P).

The Prolog interpreter responds.


P = socrates

yes

This means that Prolog was able to prove the goal by binding the variable P to socrates. This
was done by again proving someone was mortal by proving the subgoal that they were human.
Prolog thus asked if there was any P that was human. This matches against the clause
human(socrates) thereby binding P to socrates. This binding is then passed back to the parent
goal, and the results in the printout we saw above.
9
Rule 4
Sometimes we may wish to specify alternative ways of proving a particular thing. This we can
do by using different rules and facts with the same name. For example, we can represent the
sentence 'Something is fun if its a red car or a blue bike or it is ice cream' as follows:
fun(X) :- /* an item is fun if */

red(X), /* the item is red */

car(X). /* and it is a car */

fun(X) :- /* or an item is fun if */

blue(X), /* the item is blue */

bike(X). /* and it is a bike */

fun(ice_cream). /* and ice cream is also fun. */

This program says that we have three ways of finding out if something is fun. Something is fun if
it is a red and a car or blue and a bike, or if it is ice cream. These three options are represent in
Prolog by three clauses of the predicate fun. Just like we saw for pure facts, Prolog will start
from the first clause (beit a rule or fact) of fun and try that. If that does not succeed, we try the
next clause. We only fail when we run out of rules or facts to try.
Rule 5
All identically-named variables within a particular rule (e.g. all occurrences of, say, X in the first
fun rule below) are constrained to have one and the same instantiation for each solution to a
particular query. Identical variable names in separate rules are totally independent, just as if
different variable names had been used. Because the consequences of the preceding two
sentences are so fundamental to Prolog (and so often misunderstood), we recommend that you
read them again. To give an example

The program
fun(X) :-
red(X),
car(X).
fun(X) :-
blue(X),
bike(X).
Looks to Prolog Like
fun(X_1) :-
red(X_1),
car(X_1).
fun(X_2) :-
10
blue(X_2),
bike(X_2).

Thus variable name scoping is per-individual rule (often called a clause). The same variable
name may appear in different clauses of a rule, or rules with different names. Each time it is
treated as something specific to its context. A variable X may occur in the program many times
with many different bindings. It will only have the same bindings when you tell it to.
Examples of Rules 1
Consider the following program:
fun(X) :-
red(X),
car(X).
fun(X) :-
blue(X),
bike(X).
car(vw_beatle).
car(ford_escort).
bike(harley_davidson).
red(vw_beatle).
red(ford_escort).
blue(harley_davidson).

Above is both our previous program for finding fun items and facts describing some red and blue
items and some cars and bikes. Let's now use the above program and see if a harley_davidson is
fun. To do this we can ask Prolog the following question.
?- fun(harley_davidson). /* to which Prolog will reply */

yes /* to show the program succeeded */

To execute this query, Prolog will first see if harley_davidson is red, however only vw_beatles
and ford_escorts are defined as being red. Hence the query red(harley_davidson) will fail. This
means that the first clause of fun will fail. As a result, we now try the second clause of fun. This
will mean that we will attempt the subgoals blue(harley_davidson) and bike(harley_davidson).
Both these goals match facts in the database. As a result fun succeeds as we saw above.

11
Examples of Rules 2
fun(X) :-
red(X),
car(X).
fun(X) :-
blue(X),
bike(X).
car(vw_beatle).
car(ford_escort).
bike(harley_davidson).
red(vw_beatle).
red(ford_escort).
blue(harley_davidson).

We can also ask our program to find fun items for us. To do this we can pose the following
question.
?- fun(What).

To which Prolog will reply


What=vw_beatle

yes

Let's see how Prolog deals with this query. Firstly we will try the first clause of fun. This results
in us trying the goal red(What). This succeeds matching the first clause of red with the binding
What=vw_beatle. Now we attempt the goal car(vw_beatle). This matches the first clause of car,
and, as a result, the fun goal succeeds.

12
5. Query/Goal
Introducing Backtracking

Suppose that we have the following database

eats(fred,pears).

eats(fred,t_bone_steak).

eats(fred,apples).

So far we have only been able to ask if fred eats specific things. Suppose that I wish to instead answer the question,
"What are all the things that fred eats". To answer this I can use variables again. Thus I can type in the query

?- eats(fred,FoodItem).

As we have seen earlier, Prolog will answer with

FoodItem = pears

This is because it has found the first clause in the database. At this point Prolog allows us to ask if there are other
possible solutions. When we do so we get the following.

FoodItem = t_bone_steak

if I ask for another solution, Prolog will then give us.

FoodItem = apples
If we ask for further solutions, Prolog will answer no, since there are only three ways to prove fred eats
something. The mechanism for finding multiple solution is called backtracking. This is an essential
mechanism in Prolog and we shall see more of it later.

Backtracking in Rules
We can also have backtracking in rules. For example consider the following program.

hold_party(X):-
birthday(X),
happy(X).

birthday(tom).
birthday(fred).
birthday(helen).
happy(mary).
happy(jane).
happy(helen).

13
If we now pose the query
?- hold_party(Who).

In order to solve the above, Prolog first attempts to find a clause of birthday, it being the first subgoal of birthday.
This binds X to tom. We then attempt the goal happy(tom). This will fail, since it doesn't match the above database.
As a result, Prolog backtracks. This means that Prolog goes back to its last choice point and sees if there is an
alternative solution. In this case, this means going back and attempting to find another clause of birthday. This time
we can use clause two, binding X to fred. This then causes us to try the goal happy(fred). Again this will fail to match
our database. As a result, we backtrack again. This time we find clause three of birthday, and bind X to helen, and
attempt the goal happy(helen). This goal matches against clause 3 of our happy database. As a result, hold_party will
succeed with X=helen.

Query Example 1
Consider the following examples.

fun(X) :- /* something is either fun because its .... */

red(X), /* red */

car(X). /* and a car */

fun(X) :- /* or its fun if its.... */

blue(X), /* blue */

bike(X). /* and a bike */

/* database of red items */

red(apple_1).

red(block_1).

red(car_27).

/* database of cars */

car(desoto_48).

car(edsel_57).

This program says that red cars or blue bikes are fun. It then goes on to name some examples of each type of object.

14
Query Example 2
Let's now ask what is a fun item. To do this we first must enter the following query

?- fun(What).

We first attempt to prove something is fun. To do this we have two clauses of fun that we can use

/* clause 1 */

fun(X) :-

red(X),

car(X).

/* clause 2 */

fun(X) :-

blue(X),

bike(X).

Using the first clause, we can now look to see if we can find some red object

15
Query Example 3

First let's retrieve a red object from the red database

/* database of red items */

red(apple_1). first choice

red(block_1).

red(car_27).

The first red item that we find is apple_1. This gives us the instantiation X=apple_1. Next we have to see if this is a
car, so we ask the question car(apple_1). Does this match with our existing database of facts about cars?

/* database of cars */

car(desoto_48).

car(edsel_57).

The answers is that it will not. apple_1 will not match with desoto_48 or with edsel_57. So what do we do next? In
such cases, Prolog backtracks in order to find another solution. Thus we go back to the red database, and see if we
can find another possible solution. In this case we can

red(apple_1).

red(block_1). second choice

red(car_27).

We can use the next clause and see if block_1 is a red car.

16
Query Example 4
Returning to our cars database, again block_1 will not match against desoto_48 or with edsel_57. In this case we
must backtrack a second time to see if there is another red item that we can find. This time we find the third clause in
the database, and use that.

red(apple_1).

red(block_1).

red(car_27). third choice

We now attempt the car goal again, but this time for car_27. Now recall what we said earlier about the dangers of
over estimating the powers of Prolog. Prolog will try to match the goal car(car_27) against the database

car(desoto_48).

car(edsel_57).

This will fail. car_27 does not match with either desoto_48 or edsel_57. Although the programmer probably meant
car_27 to mean car number 27 to be a car, Prolog has no way of gleaning this intended semantics, and fails the call
based on a simple pattern match.

Well, what do we do now. The first step is to go back and see if there are any more possible red items. Well
we have now exhausted all three choices, hence there are no more red items to choose from. Given that there
are no more possible ways that we could satisfy clause 1, we now more on to clause 2, as we see on the next
card.

17
Query Example 5
Let us recall our program. It said that something was fun if it was red and a car or blue and a bike. Well, we couldn't
find something that was both red and a car, so we now see if something blue and a bike. Let's recap the code:

/* clause 1 */

fun(X) :- we've tried this clause but without luck

red(X),

car(X).

/* clause 2 */

fun(X) :- so lets have a go with this clause now

blue(X),

bike(X).

Thus we will now try to prove that something is fun, by trying to find something that is blue, using the blue database:

/* database of blue items */

blue(flower_3).

blue(glass_9).

blue(honda_81).

In this case we now choose blue(flower_3)

18
Query Example 6

We now have to prove the goal bike(flower_3) against our bike database:

bike(iris_8).

bike(my_bike).

bike(honda_81).

This again must fail, since flower_3 does not match against iris_8, my_bike, or honda_81. So as before, we must
backtrack and find an alternate blue item. Recall our database:

blue(flower_3).

blue(glass_9).

blue(honda_81).

We have tried flower_3 without luck, so next we try the second blue fact, blue(glass_9). We now try goal
bike(glass_9). Again this fails to unify. As a result we must go back yet again and find another blue object. This time
we will use the third clause of blue, blue(honda_81). We will now give us the goal bike(honda_81). This matches
clause three!. So at last we have found something which is blue and a bike. As a result we can now conclude that it is
fun, Prolog then displays the following:

?- fun(What).

What=honda_81

yes

19
If the user presses the semicolon (;) key, the listener looks for other solutions. First it unbinds the variable X. Next it
resumes the search using the clause following the one that had just satisfied the query. This is called backtracking.

20
21
6. Recursion
Recursion
As is commonly the case in many programming tasks, we often wish to repeatedly perform
some operation either over a whole data-structure, or until a certain point is reached. The way
we typically do this in Prolog is by recursion. This simply means a program calls itself typically
until some final point is reached. Frequently in Prolog what this means is that we have a first
fact that acts as some stopping condition followed up by some rule(s) that performs some
operation before reinvoking itself.

Recursion is often found to be a hard concept to grasp. For this reason we will present two
detailed examples of how it works.

Recursion 1
on_route(rome).
on_route(Place):-

move(Place,Method,NewPlace),
on_route(NewPlace).

move(home,taxi,halifax).
move(halifax,train,gatwick).
move(gatwick,plane,rome).

on_route is a recursive predicate. This program sees if it is possible to travel to rome from a
particular place. The first clause sees if we have already reached rome, in which case we stop.
22
The second clause sees if there is a move from the current place, to somewhere new, and then
recursive sees if the NewPlace is on_route to rome. The database of moves that we can make is
on the right.

Let's now consider what happens when we pose the query ?- on_route(home). This matches
clause two of on_route (it can't match clause one because home and rome don't unify). The
second on_route clause consists of two subgoals. The first asks whether you can move from
home to some new location i.e. move(home,Method,NewPlace). This succeeds with Method =
taxi, NewPlace = halifax. This says that yes we can move from home by taking a taxi to halifax.
Next we recursively see if we can find a route from halifax to rome by doing the same thing
again. This is done by executing the new subgoal on_route(halifax).

Recursion 2
on_route(rome).

on_route(Place):- move(Place,Method,NewPlace), on_route(NewPlace).

move(home,taxi,halifax).
move(halifax,train,gatwick).
move(gatwick,plane,rome).

The goal on_route(halifax) will fail to unify on clause one, so again we'll use the recursive clause
two and find some new place to go to. Hence we try the goal move(halifax,Method,NewPlace)
this succeeds because we can catch a train from halifax to gatwick airport. Hence Method=train,
NewPlace=gatwick. As a result we then try the recursive call on_route(gatwick), i.e. we see if
there is a move from gatwick which will get us to rome.

We now try on_route(gatwick), again this only unifies with the second clause. As a result we try
the move clause again this time with Place bound to gatwick. This query will match the third
clause of the move database. The results in Method=plane, NewPlace=rome. Next we try the
recursive goal on_route(rome). This now matches clause one of on_route. This is just a fact and
succeeds. As a result all the other on_route goals in turn succeed. Thus finally our first goal ?-
on_route(home) succeeds and Prolog responds yes

23
Recursion 3
parent(john,paul). /* paul is john's parent */

parent(paul,tom). /* tom is paul's parent */

parent(tom,mary). /* mary is tom's parent */

ancestor(X,Y):- parent(X,Y). /* someone is your ancestor if there are your parent */

ancestor(X,Y):- parent(X,Z), /* or somebody is your ancestor if they are the parent */

ancestor(Z,Y). /* of someone who is your ancestor */

The above program finds ancestors, by trying to link up people according to the database of
parents at the top to the card. So let's try it out by asking
?- ancestor(john,tom).

The first clause of ancestor looks to see if there exists a clause that could match the goal
parent(john,tom). This fails to match, as a result we try the second clause of ancestor. We now
pose the query parent(john,Z). This results in us choosing clause one of parent and binding
Z=paul. As a result, we now pose the recursive query ancestor(paul,tom). Applying the ancestor
rule again, we first try the first clause. This means we check for parent(paul,tom). which
successfully matches the second clause of parent. As a result the goal ancestor(paul,tom)
succeeds. This in turn leads to the goal ancestor(john,tom) succeeding and Prolog responding
yes

24
7. List

So far we have only considered simple items as arguments to our programs. However in Prolog
a very common data-structure is the list.

Lists themselves have the following syntax. They always start and end with square brackets, and
each of the items they contain is separated by a comma. Here is a simple list
[a,freddie,A_Variable,apple]

Prolog also has a special facility to split the first part of the list (called the head) away from the
rest of the list (known as the tail). We can place a special symbol | (pronounced 'bar') in the list to
distinguish between the first item in the list and the remaining list. For example, consider the
following.
[first,second,third] = [A|B]
where A = first and B=[second,third]

The unification here succeeds. A is bound to the first item in the list, and B to the remaining list.

List Examples 1
Here are some example simple lists

[a,b,c,d,e,f,g]

[apple,pear,bananas,breadfruit]

[ ] /* this is a special list, it is called the empty list because it contains


nothing */

Now lets consider some comparisons of lists:

[a,b,c] unifies with [Head|Tail] resulting in Head=a and Tail=[b,c]

[a] unifies with [H|T] resulting in H=a and T=[]

[a,b,c] unifies with [a|T] resulting in T=[b,c]

[a,b,c] doesn't unify with [b|T]

[] doesn't unify with [H|T]

[] unifies with []. Two empty lists always match

25
List Example 2
Consider the following fact.

p([H|T], H, T).

Lets see what happens when we ask some simple queries.

?- p([a,b,c], X, Y).

X=a

Y=[b,c]

yes

?- p([a], X, Y).

X=a

Y=[]

yes

?- p([], X, Y).

no

List Searching
We can use lists within facts and rules. One common way of using lists is to store information
within a list and then subsequently search for this information when we run our programs. In
order to search a list, Prolog inspects the first item in a list and then goes on to repeat the same
process on the rest of the list. This is done by using recursion. The search can either stop when
we find a particular item at the start of the list or when we have searched the whole list, in
which case the list to be searched will be the empty list. In order to do this, we have to be able
to selectively pull the list apart. We have already seen how we can go about doing this. In the
previous section we showed how to take the head and a tail of a list:
[Head|Tail]

26
This method constitutes the basis of the searching method. We shall use it to pull apart a list,
looking at the first item each time, recursively looking at the tail, until we reach the empty list
[ ], when we will stop.

List Searching: Example 1


Consider the following problem. How can I see if a particular item is on a particular list? For
example I want to test to see if item apples is on the list [pears, tomatoes, apples, grapes]. One
possible method of doing this is by going through the list, an item at a time, to see if we can
find the item we are looking for. The way we do this in Prolog is to say that we could definitely
prove an item was on a list if we knew that the target item was the first one on the list. ie.

on(Item,[Item|Rest]). /* is the target item the head of the list */

Otherwise we could prove something was on a list if we could prove that although it didn't
match the existing head of the list, it nonetheless would match another head of the list if we
disregarded the first item and just considered the rest of the list i.e.

on(Item,[DisregardHead|Tail]):- on(Item,Tail).

We now have a program consisting of a fact and a rule for testing if something is on a rule. To
recap, it sees if something is the first item in the list. If it is we succeed. If it is not, then we
throw away the first item in the list and look at the rest.

List Searching: Example 2


on(Item,[Item|Rest]).

on(Item,[DisregardHead|Tail]):- on(Item,Tail).

Let's go through the last example again in more detail. Suppose we pose the query:

?- on(apples, [pears, tomatoes, apples, grapes]).

The first clause of on requires the first argument of on to match with the list head. However
apples and pears do not match. Thus we must move on to the second clause. This splits the list
up as follows:
DisregardHead = pears

Tail = [tomatoes,apples,grapes]

This now gives us the following goal in the body of our rule:
27
on(apples, [tomatoes, apples, grapes]).

Again, we see if apples and tomatoes match using our initial facts. Since they don't, we again
use our second clause to strip off the current list head, giving us the new goal: on(apples,
[apples,grapes]). Since apples matches apples our first clause succeeds as does our query.

List Searching: Exercise


Given the following list of cities and features

[london_buckingham_palace,
paris_eiffel_tower,
york_minster,
pisa_leaning_tower,
athens_parthenon]

Write a program called member, that would be able to see if something was a member of a list.
The program would inform me that sydney_opera_house was not on list by failing, yet confirm
all the above were on the list by succeeding (answering yes).

List Construction.
The last example showed how we could strip off the front items on a list and recursively search
the rest. We can also use the same method to build lists. For example take one existing list, say
List1. We can make a new list List2 which contains List1 but with the new head prolog, as
follows:
List2 = [prolog|List1]

This again is pure unification. List2 now consists of the head prolog plus whatever List1 was
bound to. If List1 were to be bound e.g. List1 = [list,c,pascal,basic], List2 would now be the list
[prolog,lisp,c,pascal,basic]

We can use this construction technique to build lists during recursion. We'll present three
examples of this in the forthcoming cards.

List Construction Example 1.


An example use of list construction is when we wish to create a new list out of two existing lists.
We'll illustrate this by defining a predicate called append that takes three arguments. The first

28
two are lists. The predicate uses these two lists to produce a third list which combines the
original two. For example,

?- append([a,b,c],[one,two,three],Result).

Result = [a,b,c,one,two,three]

The way we do this in Prolog uses both list construction and list deconstruction. Recall that on
the previous card we saw how we could glue an item onto the front of a list to produce a new
list. Thus we can produce the list [c,one,two,three] from the list [one,two three] by saying
NewList = [c|[one,two,three]. This results in NewList being bound to the list [c,one,two,three].
This is the clue to how we write append in Prolog. We can recursively split the first list into
single items by the deconstruction techniques discussed earlier. We can then use the
construction method above to glue together a new list, which we shall illustrate next.

List Construction Example 2


In order to write append we shall first search through the first list, and taking each item in turn,
add it to the second list. This we'll do recursively, by searching for the end of the first list, and
then adding the items in the first list to the items in the second list in reverse order. Thus the
last item in the first list is the first to be added to the second list. We illustrate this more clearly
with an example. First lets see what the code to do this look like. First we must deal with the
case when the first list is an empty list. In this case the result of appending the two lists will just
be the second list. Thus this gives
append([],List,List).
Now for the rule which does most of the work. This says search through the first list can each
time stick the first thing on the first list onto the resulting list.
append([Head|Tail],List2,[Head|Result]):- append(Tail,List2,Result).

Now lets go through in detail how this actual works.

List Construction Example 3


Recall our code and query ?- append([a,b,c],[one,two,three],Result).
append([],List,List).

append([Head|Tail],List2,[Head|Result]):- append(Tail,List2,Result).

Let's now follow the program as it executes. Using the second rule we first reduce the query to
29
append([b,c],[one,two,three],Result)

and then to
append([c],[one,two,three],Result)

and finally to
append([],[one,two,three],Result).

This final clause can match against the initial fact, giving append([],[one,two,three],
[one,two,three]).

Since this is a fact, this terminates the recursion. As we pop out of each recursive step we then
add on (respectively) c,b, and a to the list, giving the list [a,b,c.one,two,three].

List Construction Example 4


Let's now consider a second example of constructing lists. Taking the list [1,12,3,14,5,8], lets
construct a new list out of the old that contains only numbers greater than 6. To do this we can
search the list as we have seen before. If the item passes our test, then we can add it to the
output list. If it does not, then we can disgard the item. To make a start, if the list to search is
empty, we will return the empty list. Hence we get the base case of the recursion.

sift([],[]).

Next we need to say that if the item passes our test, put it in the output list

sift([X|T],[X|Result]):- X > 6, /* is X greater than 6 */

sift(T,Result). /* if so then go find the rest */

Otherwise we are will disgard the head and look for other hits in the tail

sift([ThrowAway|Tail],Result):- /* disgard the head */

sift(Tail,Result). /* and look in the tail */

30
List Constructions Example 5
Let us now see how our program respond to the query

?- sift([1,12,3,14,5,8], Result).

To this goal, we first unify with clause 2. The result is to evaluate the subgoal 1 > 6, this clearly
fails, so we move on to the third clause and try the goal sift([12,3,14,5,8],Result). This again
matches on the second clause of sift. We now try the subgoal 12>6, this succeeds and we now
attempt the goal sift([3,14,5,8],Result). However, notice what also happens. By using this clause
we also place 12 on our output list.

Stepping further through our example, we see that greater than subgoal in clause 2 succeeds for
14 and 8, till finally we get the goal sift([],Result). This matches the first goal, with Result bound
to []. As we come out of the recursion, we see that clause 2 builds up the result for to the list [8],
then [14,8], then finally [12,14,8], before the query finally succeeds.

31

You might also like