Prolog Tutor

download Prolog Tutor

of 39

Transcript of Prolog Tutor

  • 8/12/2019 Prolog Tutor

    1/39

    Prolog Self Tutor

    Dave Saunders

    (modified by Graem Ringwood)

    Department of Computer Science

    Queen Mary and Westfield College

    0.

    The notes are based on four weeks' of practical. Most statements in these notes hold for allversions of Prolog. They use ISO Prolog.

    0.1 References

    There are many books on Prolog and AI. The following are the better ones:

    Flach P (1994) Simply Logicial, Wiley

    O'Keefe (1990) The Craft of Prolog, MIT Press

    Shoham Y (1994) Artificial Intelligence Techniques in Prolog, Morgan Kaufmann

    *Sterling LS and Shapiro EY (1994) The Art of Prolog 2nd ed, MIT Press

    The one marked with a * is introductory.

    0.2 Accessing Prolog

    Under Unix, Prolog may be accessed by typing:

    prolog

    in an Xterm or telnet session. If there is a prolog system running on the machine you will geta prompt something like:

    SICStus 2.1 #8: Wed Jan 19 12:23:17 GMT 1994

    | ?-

    There is an online manual for Sicstus Prolog which can be accessed on the Web with theURL: http://www.dcs.qmw.ac.uk:8080/manuals/SICStus_man/sicstus_toc.html

    Sicstus Prolog is bothcompiledandinterpreted. Programs are usually loaded from files. Anumber of procedures arebuilt-in, that is they are present before any user program is loaded.One of these isconsult, which loads a program from a file Type:

    consult(file).

    in response to the prompt to load the file called file.pl.Note the full-stop at the end.Byconvention, Prolog filenames end in .pl. It is not necessary to type this extension. If thefilename starts with a capital letter, or contains non-alphanumeric characters, you have toenclose it within quotes:

    consult('file.pl').

    To exit the Prolog system type:

    halt.

    or

    ^D.

    A summary of all the built-ins is given in the manual.

    1

  • 8/12/2019 Prolog Tutor

    2/39

    1. Week 1

    1.1. Introduction

    Prolog is a relational language. It is a based on a subset of FOL. There are some non-logicalparts for finer control of program execution.

    1.2. The usual exampleThe following is a Prolog program representing a family database. This is the usual firstProlog program.

    mother(fred,jill).

    mother(flo,jerri).

    mother(terry,jerri).

    mother(james,rowena).

    mother(jill,roberta).

    mother(rob,henrietta).

    mother(harry,petra).

    father(fred,james).

    father(flo,james).

    father(terry,jack).

    father(james,rob).

    father(jill,rex).

    father(rob,harry).

    father(rex,harry).

    father(harry,peter).

    parent(X,Y) :-mother(X,Y).

    parent(X,Y) :-father(X,Y).

    grandparent(X,Y) :-parent(X,Z),parent(Z,Y).

    ancestor(X,Y) :-parent(X,Y).

    ancestor(X,Y) :-parent(X,Z),ancestor(Z,Y).

    All predicates are to be read in similar fashion egfather(fred,james) is read 'fred is the fatherof james'. The family tree above looks like:

    2

  • 8/12/2019 Prolog Tutor

    3/39

    fred o terry

    jackjerrijill james

    roberta rex rowenarob

    henriettaharry

    petrapeter

    1.3. Prolog syntax

    A program is made up of suchfacts andrules. Both are called definite clauses. Within thesearepredicate expressions such asmother(james,rowena) andancestor(X,Z). A predicateexpression consists of a predicate name followed by the arguments in parentheses, separatedby commas. If there are no arguments then the parentheses are omitted. All the predicatesexpressions in the above example have two arguments but there may be any number.Names of objects may contain the underline symbol _, letters and digits, but cannot startwith a digit. Names starting with a lower case letter are constants and functions. Namesstarting with capital letters or underline represent variables and are assumed to beuniversally quantified. The scope of a variable is the definite clause. Strings like 123 or -432are integers in the usual way. Definite clauses are terminated with a period and a carriagereturn, as can be seen above.

    A fuller account of the syntax is given in the manual.

    1.4. Logical interpretation

    A Prolog rule, of the form:

    p(X1,...Xn) :- q(X1,...Xn,Y1,...Ym)

    has the logical reading

    (X1,...Xn)(p(X1,...Xn)(Y1,...Ym)(q(X1,...Xn,Y1,...,Ym)))

    or alternatively

    (X1,...Xn,Y1,...Ym)(p(X1,...Xn)q(X1,...Xn,Y1,...,Ym))Thus the last statement of the program has the logical reading

    (X,Y,Z)(ancestor(X,Y)parent(X,Z)ancestor(Z,Y))

    Note therefore that a Prolog statement implicitly has universal quantification over allvariables in it. Logicalorcan be written using two Prolog statements.

    To say that "X is theparentof Y if X is themotherof Y OR X is thefatherof Y"

    (X,Y)(parent(X,Y)mother(X,Y)father(X,Y))

    would appear in Prolog as the two rules

    parent(X,Y) :-mother(X,Y).

    3

  • 8/12/2019 Prolog Tutor

    4/39

    parent(X,Y) :-father(X,Y).

    Note that a fact such asfather(fred,james) can be thought of as a rule (father(fred,james) :- .),ie, fred is thefather ofjames if ..., ie you don't need any condition to prove that the father offred is james. A rule such as ( :- p(X).) is equivalent to nothing can be concluded from p(X),ie p(X) is false. This is more easily seen if we note the following equivalences:

    ISO Prolog logic

    q :- p. qp

    and hence

    q :- p. qp

    q :- . q

    :- p. p

    1.5. The Prolog interpreter.

    1.5.1. Getting StartedCopy the file /import/teaching/AdvMSc/MAS/family.pl to your own directory. Start upthe Prolog interpreter as described above and consult the file family. This is done byresponding

    consult(family).

    to the prompt. A shorter way is simply [family]..You MUST put a period '.' at the end.The interpreter then checks for syntax errors etc, but if there are none it replies with amessage indicating the consultation followed by:

    yes

    |?-

    Theyesmeans that it has satisfied the consultation. If there were errors it would haveindicated the position of the first error and then saidno. Anything you respond to theprompt is regarded as a query which it tries to satisfy. The last line is then just anotherprompt.

    A fuller account of how to run Prolog is given in the maual.

    1.5.2. Querying facts

    You can now query the fact and rule base.

    Thus

    |?-father(flo,james).

    yes

    (REMEMBER TO PUT THE '. 'AT THE END.)

    |?-father(fred,flo).

    no

    |?-

    4

  • 8/12/2019 Prolog Tutor

    5/39

    If you give a query with variables in it, the system tries to find all ways of satisfying thequery - after each success it asks whether you wish it to continue. If you want any otheranswers type;if not type.. Thus:

    |?-mother(fred,X).

    X = jill ;

    no

    |?-mother(X,jerri).

    X = flo ;

    X = terry ;

    no

    |?-father(X,james).

    X = fred .

    yes

    |?-

    We could also have triedfather(X,Y) which would have yielded all pairs X,Y where thefatherof X is Y, eg

    |?-father(X,Y).

    X = fred

    Y = james ;

    X = flo

    Y = james ;

    X = terry

    Y = jack .

    yes

    |?-

    1.5.3. Querying facts derived from rules.

    This is essentially the same, thus:

    |?-parent(fred,M).

    M = jill ;

    5

  • 8/12/2019 Prolog Tutor

    6/39

    M = james ;

    no

    |?-parent(M,james).

    M = fred ;

    M = flo ;

    no

    |?-

    The system tries to satisfyparent(fred,M). Accordingly it runs through the facts and rulesuntil it findsparentin a head of a rule. The first such isparent(X,Y) :-mother(X,Y).. Itunifiesfred with X (more on unification later) and tries to satisfy the body of the rule with thisunification ie it tries to satisfymother(fred,M). To do this it runs through the knowledgebase till it comes tomother(fred,jill) which gives us the first solution M = jill. On receiving

    the semicolon from us it tries again, but can't find any other occurences ofmotherwith fredas first argument. It then looks again for a rule head containingparent. The next such isparent(X,Y) :-father(X,Y). so it tries to satisfyfather(fred,M) which gives another solution M= james. On receiving another semicolon it tries again, but can't satisfyparent(fred,M) inany other way. Trying to satisfyparent(M,james) is similar but this timemothernever canbe satisfied with james as second argument, butfathercan be satisfied twice.

    Note that the queryparent(terry,jack) would first try to satisfymother(terry,jack) which fails,and thenfather(terry,jack) which succeeds, thus:

    |?-parent(terry,jack).

    yes

    |?-parent(james,fred).

    no

    |?-

    Using the other predicates:grandparent,ancestoris similar.

    |?-grandparent(fred,rowena).

    yes

    |?-grandparent(X,james).

    no

    |?-ancestor(X,fred).

    no

    |?-grandparent(james,B).

    B = henrietta ;

    6

  • 8/12/2019 Prolog Tutor

    7/39

    B = harry ;

    no

    |?-grandparent(X,rob).

    X = fred ;

    X = flo ;

    no

    |?-

    Of course, james probably has 4grandparents, but only two of them seem to be in theknowledge base. To satisfygrandparent(james,B) it finds the rulegrandparentetc and triesto satisfyparent(james,Z) which it first does by trying to satisfymother(james,Z) whichsucceeds with Z=rowena. It then tries to satisfyparent(rowena,B) but fails. It thenbacktracks

    to the previous predicate ieparent(james,Z) and tries to resatisfy this. Needless to say jamesdoesn't have anothermotherso it tries using the otherparentrule ie tries to satisfyfather(james,Z). It can do this by setting Z=rob. It then triesparent(rob,B). It can do this bysatisfying mother(rob,B) with B = henrietta. After the semicolon it then satisfiesparent(rob,B) again usingfather(rob,B) with B = harry. It tries again after the nextsemicolon, but there are no more solutions.

    Grandparent(X,rob) is done similarly. It tries to satisfyparent(X,Z) and hencemother(X,Z).This can be done with X=fred,Z=jill. However,parent(jill,rob) then fails. It then triesX=flo,Z=jerri but thenparent(jerri,rob) fails. This continues until it runs out of ways ofsatisfyingmother(X,Z), and triesfather(X,Z) instead. It satisfies this with X=fred,Z=james,and it tries to satisfyparent(james,rob). This succeeds asfather(james,rob) succeeds.grandparent(X,rob) therefore succeeds with X= fred. The next attempt obtains the othersolution, and any further attempt fails.

    You can watch all these contortions by doing

    |?- trace.

    yes.

    |?-grandparent(X,rob).

    Note the recursive definition ofancestor. Logically the interpretation is

    (X,Y)(ancestor(X,Y)parent(X,Y))

    (X,Y)(ancestor(X,Y)((Z)(parent(X,Z)ancestor(Z,Y)))

    ie

    (X,Y)(ancestor(X,Y)parent(X,Y)(Z)(parent(X,Z)ancestor(Z,Y)))

    or, moving all quantification to the front,

    (X,Y,Z)(ancestor(X,Y)(parent(X,Y)(parent(X,Z)ancestor(Z,Y)))

    which is easier to read.

    Operationally, if we enter a queryancestor(fred,james), the interpreter first tries to satisfyparent(fred,james), and to do this it first tries to satisfymother(fred,james) and thenfather(fred,james) which succeeds causingparent(fred,james) and thenancestor(fred,james)

    7

  • 8/12/2019 Prolog Tutor

    8/39

    to succeed. If we enter a queryancestor(jill,petra) it tries to satisfyparent(jill,petra) asindicated above and, when this fails, tries the other rule forancestor. Thus it tries to satisfyparent(jill,Z) usingmother(jill,Z), which can be done with Z=roberta. It then tries to satisfyancestor(roberta,petra). To do this it first triesparent(roberta,petra) which fails, as bothmother(roberta,petra) andfather(roberta,petra) fail. It then tries the other definition forancestor. It triesparent(roberta,Z) but finds no occurences of eithermotherorfatherwithroberta as first argument.ancestor(roberta,petra) then fails, and it backtracks to

    mother(jill,Z). There is no othermotherfor jill, and so it tries the other possibility forparent(jill,Z) ie father(jill,Z). This succeeds with Z=rex, and it tries to satisfyancestor(rex,petra).parent(rex,petra) fails so it tries the second definition forancestor.parent(rex,Z) succeeds with Z=harry, so it goes on toancestor(harry,petra). This timeparent(harry,petra) succeeds asmother(harry,petra) does, soancestor(jill,petra) succeeds.The sequence of calls were as follows (as indicated by trace) where I have added theindentation to improve readability. Note that eachCallis matched with aFailor anExit(asuccess) -Backto corresponds to another attempt to satisfy a predicate that has beenpreviously attempted, but failed.

    (1) 1 Call:ancestor(jill,petra) ?

    (2) 2 Call:parent(jill,petra)

    (3) 3 Call:mother(jill,petra) ? (3) 3 Fail:mother(jill,petra)

    (4) 4 Call:father(jill,petra) ?

    (4) 4 Fail:father(jill,petra)

    (2) 2 Fail:parent(jill,petra)

    (5) 5 Call:parent(jill,_65637) ?

    (6) 6 Call:mother(jill,_65637) ?

    (6) 6 Exit:mother(jill,roberta)

    (5) 5 Exit:parent(jill,roberta)

    (7) 5 Call:ancestor(roberta,petra) ?

    (8) 6 Call:parent(roberta,petra) ?

    (9) 7 Call:mother(roberta,petra) ?

    (9) 7 Fail:mother(roberta,petra)

    (10) 8 Call:father(roberta,petra) ?

    (10) 8 Fail:father(roberta,petra)

    (8) 6 Fail:parent(roberta,petra)

    (11) 9 Call:parent(roberta,_65666) ?

    (12) 10 Call:mother(roberta,_65666) ?

    (12) 10 Fail:mother(roberta,_65666)

    (13) 11 Call:father(roberta,_65666) ?

    (13) 11 Fail:father(roberta,_65666)

    (11) 9 Fail:parent(roberta,_65666)

    (7) 5 Back to:ancestor(roberta,petra) ?

    (7) 5 Fail:ancestor(roberta,petra)

    (6) 6 Back to:mother(jill,_65637) ?

    (6) 6 Fail:mother(jill,_65637)

    (14) 12 Call:father(jill,_65637) ?

    (14) 12 Exit:father(jill,rex)

    8

  • 8/12/2019 Prolog Tutor

    9/39

    (5) 5 Exit:parent(jill,rex)

    (15) 5 Call:ancestor(rex,petra) ?

    (16) 6 Call:parent(rex,petra) ?

    (17) 7 Call:mother(rex,petra) ?

    (17) 7 Fail:mother(rex,petra)

    (18) 8 Call:father(rex,petra) ? (18) 8 Fail:father(rex,petra)

    (16) 6 Fail:parent(rex,petra)

    (19) 9 Call:parent(rex,_65666) ?

    (20) 10 Call:mother(rex,_65666) ?

    (20) 10 Fail:mother(rex,_65666)

    (21) 11 Call:father(rex,_65666) ?

    (21) 11 Exit:father(rex,harry)

    (19) 9 Exit:parent(rex,harry)

    (22) 9 Call:ancestor(harry,petra) ?

    (23) 10 Call:parent(harry,petra) ?

    (24) 11 Call:mother(harry,petra) ?

    (24) 11 Exit:mother(harry,petra)

    (23) 10 Exit:parent(harry,petra)

    (22) 9 Exit:ancestor(harry,petra)

    (15) 5 Exit:ancestor(rex,petra)

    (1) 1 Exit:ancestor(jill,petra)

    A fuller account of debugging is given in the manual.

    1.6. More exercises

    1) Set up a family knowledge base with facts

    male(fred).

    male(james).

    female(jill).

    female(flo).

    parent(fred,james).parent(fred,jill).

    parent(flo,james).

    and rule

    father(X,Y)male(Y),parent(X,Y).

    Make up a few more rules reflecting other family relationship nomenclature, eggreatgrandparent,uncle,aunt,sister,brotherandsibling. Try various queries on thisknowledge base. Try using trace before entering a query to see the effect of the query (pressRETURN whenever you get the ? prompt when tracing - you could try some of the othertrace features in the manual).

    2) Consider the following diagram.

    9

  • 8/12/2019 Prolog Tutor

    10/39

    D

    C

    B

    A E

    F

    G

    H

    a) Represent in Prolog the facts describing the relationships between the named blocks. Usetwo predicates:above1which says that a block is immediately above another andbaseleft1which says that a block is immediately to the left of another. Only give factsof the second type for blocks which are at the bottom of stacks.

    b) Write another predicateabovewhich is true whenever its first argument is a blockanywhere in a stack above the block given in the second argument.

    c) Write another predicatestackleft1when a block is in a stack which is immediately to theleft of the second block.

    d) Write predicatesbaseleftandstackleftwith the same meaning asbaseleft1andstackleft1except that the first block can be any number of blocks to the left of the second block.

    e) Try a few queries.

    3) Try some other exercises from a Prolog book.

    4) Look atProgramming Examplesin the Sicstus manual web page.

    To leave a Prolog session type CONTROL d.

    10

  • 8/12/2019 Prolog Tutor

    11/39

    2. Week 2

    2.1. Multicondition queries

    If in the previous example family knowledge base of Week 1 we wished to know aboutgreatgrandparents, we could use a multicondition query:

    |?-parent(X,Z),grandparent(Z,Y).

    and this would give us all triples X,Y,Z which satisfy both predicate expressions. Similarly

    |?-parent(flo,Z),grandparent(Z,Y).

    would give us all the Z,Y pairs satisfying both predicate expressions.

    Any valid body of a clause is a valid query. Such a query could be used to define a

    predicate:

    greatgrandparent(X,Y) :-parent(X,Z),grandparent(Z,Y).

    2.2. Order

    2.2.1. Order of facts with the same predicate nameWe saw from the family example that the Prolog system gives the answers in the order thatthey were written in the file. Thus with

    father(fred,james).

    father(flo,james).

    and the queryfather(X,james) we are given the solutions in the order fred, flo. This normallywill not matter as in most cases we wish to find all possible solutions.

    2.2.2. Order of rules with the same head

    Similarly, given rules

    parent(X,Y) :-mother(X,Y).parent(X,Y) :-father(X,Y).

    and the queryparent(fred,X), we get the solutions in the order jill, james because it firstsatisfiesparent(fred,X) usingmother(fred,X), and then usingfather(fred,X). In this case itdoes not matter as we wish allparents. In some cases it simply changes the amount of workthat needs to be done by the system. This is not considered good programming practice andshould be avoided if possible.

    2.2.3. Order of predicate expressions inside a query

    The order of predicates expressions in a query has the operational interpretation ofsequencing: "satisfy the firstand thensatisfy the second". This can have unfortunate effectsin the search for a solution. While in the logical reading the order does not matter, inpractice the search can be made more efficient by a suitable ordering of the atoms. A generalrule is situate those prediates with fewer soluitons to the left.

    2.3. Lists

    2.3.1. Constant lists and simple queries.

    Prolog provides for handling linked lists. These are denoted in Prolog using (square)brackets with commas separating the individual elements in the list, eg:

    [monday,tuesday,wednesday,thursday,friday,saturday,sunday]

    Thus we might have the following facts

    weekdays([monday,tuesday,wednesday,thursday,friday]).

    11

  • 8/12/2019 Prolog Tutor

    12/39

    weekend([saturday,sunday]).

    Consider the following Prolog session

    |?-weekdays(D).

    D = [monday,tuesday,wednesday,thursday,friday] ;

    no

    |?-weekend(X).

    X = [saturday,sunday] .

    yes

    |?-weekend([X,Y]).

    X = saturday

    Y = sunday ;

    no

    |?-weekend([saturday,X]).

    X = sunday ;

    no

    |?-weekend([sunday,saturday]).

    no

    |?-weekend([saturday,sunday]).

    yes

    |?-weekend([X]).

    no

    |?-weekend([X,Y,Z]).

    no

    Thus the query succeeds if the argument in the query can be unified (ie matched)appropriately with the argument in the fact. Note that it is treated strictly as a list, and not a

    set ie [saturday,sunday][sunday,saturday]. Similarly, note that [a,a][a]. Note that[X,Y] will only match with lists of length 2, [X] will only match with lists of length 1, X willmatch with lists of any length.

    12

  • 8/12/2019 Prolog Tutor

    13/39

    2.3.2. Head & Tail representation

    A list of one or more elements can be represented using [X|Y], where the bar (|) separatesthe head and tail of the list. The bar can be put after any number of elements, and the nameput after it refers to the rest of the list from that point. [] denotes the empty list. Thus

    |?-weekdays([X|Y]).

    X = monday

    Y = [tuesday,wednesday,thursday,friday] ;

    no

    |?-weekdays([X,Y,Z|W]).

    X = monday

    Y = tuesday

    Z = wednesdayW = [thursday,friday] ;

    no

    |?-weekend([X,Y|W]).

    X = saturday

    Y = sunday

    W = [] ;

    no

    2.3.3. Common list predicates

    Consider the following predicates

    first([X|L],X).

    tail([X|L],L).

    last([X],X).

    last([Y|L],X) :-last(L,X).

    member(X,[X|L]).

    member(X,[Y|L]) :-member(X,L).

    The first of these is equivalent to "first(Y,X) is true iff Y has the form [X|L]" ie the head of thelist given as the first argument is the same as the value in the second argument. Thus it actsas a deduction rule even though it is expressed as a fact. This relies on Prolog's unificationabilities, which are more extensive than those of other languages such as ML or Miranda. Asimilar comment applies to the predicatetail. Thus:

    |?-first([a,b,c],a).

    yes

    13

  • 8/12/2019 Prolog Tutor

    14/39

    |?-first([b,c,d],a).

    no

    |?-first([a,b,c],X).

    X = a ;

    no

    |?-first([M,b,c],a).

    M = a ;

    no

    |?-first([a|P],a).

    P = _0 ;

    no

    |?-first([X],a).

    X = a ;

    no

    |?-first(X,a).

    X = [a|_6] ;

    no

    |?-first([X|L],X).

    X = _0

    L = _1 ;

    no

    |?-first([X|L],Y).

    X = _0

    L = _1

    Y = _0 ;

    no

    14

  • 8/12/2019 Prolog Tutor

    15/39

    Thus Prolog tries to unify the first argument with the [X|L] and the second with X. In thefirst example it was able to do this with X=a, and L=[b,c]. In the second there was no way todo it. In all other cases there was only one way in which it could do this. In some of thesethe list is not fully bound to a constant, but variables such as _0, _6 are involved. Ignore thenumbers in these - they are actually meaningless, except where they indicate that two valuesare the same. The third last example shows that it can be done, if X is a list withaat the head,with any tail. The second last says again that it can be done if X,L were instantiated to

    anything (though L would have to be a list - this is not indicated). In the last the solutionsays it can be done if X and Y have the same value.

    Thetailpredicate behaves similarly.

    |?-tail([],L).

    no

    |?-tail([a],L).

    L = [] ;

    |?-tail([X|a],a).

    X = _0 ;

    |?-tail([X,b,c],L).X = _0 L = [b,c] ;

    |?-tail([X,b,c],[b,Y]).

    X = _0 Y = c ;

    |?-tail([X,b,c],[b|Y]).

    X = _0 Y = [c] ;

    The above predicates were given their definition as facts, the next two are recursivedefinitions, with one of them essentially a stopping rule.

    last([X],X).

    last([Y|L],X) :-last(L,X).

    member(X,[X|L]).

    member(X,[Y|L]) :-member(X,L).

    Operationally they work by trying to match the arguments of the query with the argumentsin the heads of the definition.

    Consider two queries, the first beinglast([w],w), and the secondlast(P,w). The first works asfollows:last([w],w) can unify [X] with [w] by X=w which then fits the second argument aswell so the query is satisfied. The querylast(P,w) would use the first definition yieldingP=[w]. On being requested to find other solutions, it would try the second rule, unifying

    X=w and Y,L temporarily with variables; it would try to find these Y,L by satisfying thequerylast(L,X) in the body of the rule. X=w already, so it tries to find an L so thatlast(L,w)succeeds. To do this it starts again with the first rule, satisfying it with L=[w]. This gives asecond solution P=[_5,w]. The system will continue finding solutions with progressivelylonger lists with w in the final position, and variables in the other positions.

    The effect of the querylast([a,b,c],M) can be seen from the following trace

    |?-last([a,b,c],P).

    (1) 1 Call:last([a,b,c],_6) ?

    (2) 2 Call:last([b,c],_6) ?

    (3) 3 Call:last([c],_6) ?

    (3) 3 Exit:last([c],c)

    (2) 2 Exit:last([b,c],c)

    15

  • 8/12/2019 Prolog Tutor

    16/39

    (1) 1 Exit:last([a,b,c],c)

    P = c ;

    (3) 3 Back to:last([c],_6) ?

    (4) 2 Call:last([],_6) ?

    (4) 2 Fail:last([],_6) (3) 3 Fail:last([c],_6)

    (2) 2 Fail:last([b,c],_6)

    (1) 1 Fail:last([a,b,c],_6)

    no

    |?-

    where I've added the indentation for readability. Thus it works its way to the end of the listusing the recursive definition, until it satisfies the first clause oflast, and then exits with thefirst solution. On retrying, it starts where it stopped and tries the recursive definition again.The call fails, which causes all the calls it was embedded in to fail.

    Note that both rules forlastcould have been given with the same head by writing the first as

    last([Y|L],X) :- X=Y,L=[].

    where = is a built-in predicate that denotes equality. In Prolog equality is syntatic so X=Y isunderstood as X unfies with Y. For example, [c,X,b] = [c,d|L] by setting X=d,L=[b].

    Note that a querymember(X,[a,b,c]) will have 3 solutions for X and the querymember(X,[a,b,a,a,a,c]) will have 6 solutions for X. The querymember(X,[]) will simply fail as didlast([],X). Remember that queries can be combined, so we can find the last weekday by

    |?-weekdays(D),last(D,X)

    2.3.4 Other useful list predicatesTwo lists can be appended by progressively sticking the elements of the first onto the thesecond (using first, for example). It is easier using the following, which takes advantage ofunification:

    concatenate([],L,L).

    concatenate([X|L1],L2,[X|L3]) :-concatenate(L1,L2,L3).

    where the second rule reads as

    "if L3 is L1 concatenated onto L2 then [X|L3] is [X|L1] concatenated onto L2".

    The examples above showed that predicates could be used in several ways: with various ofthe arguments either bound to constants (eg w, [a,b,c]), completely unbound (ie as a simple

    variable eg M), given as a structure with its parts unbound (eg [X|L]), or given as a structurewith only some of its parts unbound (eg [a,X,b|L]).

    If a predicate has been written properly, this will always be the case. This is usually easy ifwe can get a strict logical interpretation to what we have written. Considerconcatenate, andonly the possibilities of completely bound or unbound. The following (slightly abbreviated)examples show what happens.

    |?-concatenate([a,b],[c,d],[a,b,c,d]).

    yes

    |?-concatenate([a,b],[c,d],L).

    L = [a,b,c,d]

    |?-concatenate([a,b],L,[a,b,c,d]).

    16

  • 8/12/2019 Prolog Tutor

    17/39

    L = [c,d]

    |?-concatenate(L,[c,d],[a,b,c,d]).

    L = [a,b]

    |?-concatenate(L,M,[a,b,c]).

    L=[] M=[a,b,c] ;

    L=[a] M=[b,c] ;L=[a,b] M=[c] ;

    L=[a,b,c] M=[] ;

    |?-concatenate(L,[a,b],M).

    L=[] M=[a,b] ;

    L=[_1] M=[_1,a,b] ;

    L=[_1,_2] M=[_1,_2,a,b]

    etc till we tell it to stop

    |?-concatenate([a,b],L,M).

    L=_0 M=[a,b|_0]

    |?-concatenate(L,M,N).

    L=[] M=_1 N= _1 ;

    L=[_8] M=_1 N=[_8|_1]

    etc till we tell it to stop

    It will also work with some of its arguments partially bound, egconcatenate([a,X],[b|L],[Y,c,W,d,X]) will be satisfied with X=c, Y=a, W=b, L=[d,c]. This ability to use properlywritten predicates in many ways makes logic programming powerful - you do not have towrite lots of separate functions.

    2.4. Lists of listsWe can have have lists embedded in lists. These can be dealt with similarly. Thus we coulddefinedeepmember(X,L) to mean that X is an element of L or one of its sublists by

    deepmember(X,[Y|L]) :- X=Y.

    deepmember(X,[Y|L]) :-deepmember(X,Y).

    deepmember(X,[Y|L]) :-deepmember(X,L).

    Note thatdeepmember(X,[[a,b],[[c,[d]],e]) has as solutions all of

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

    as, for example, [a,b] is an element of the original list. If we only wanted individual items

    then we would also have to specify that X was not a list, ie that it did not have the form [Z|W], as we shall see later this is not the same as saying that it is anatom. Thus we might have

    deepatommember(X,M) :-deepmember(X,M),\+X=[Z|W].

    where \+ X=Y means Xnot equal toY. As equality in Prolog is syntatic,\+X=Y means thatthe term Xdoes not unify with the termY. (There will be more on negation later.)

    We could substitute in the definition ofdeepmemberto obtain

    deepatommember(X,[X|L]):-X=Y.

    deepatommember(X,[Y|L]) :-deepmember(X,Y).

    deepatommember(X,[Y|L]) :-deepmember(X,L)), \+ X=[Z|W].

    This still contains calls todeepmember, and we might like to remove these so that

    deepatommemberrecurses only on itself. It then becomes

    deepatommember(X,[Y|L]) :- X=Y.

    17

  • 8/12/2019 Prolog Tutor

    18/39

    deepatommember(X,[Y|L]) :-deepatommember(X,Y).

    deepatommember(X,[Y|L]) :-deepatommember(X,L)), \+ X=[Z|W].

    The querydeepatommember(X,[[a,b],[[c,[d]],e]) has solutions a,b,c,d,e.

    2.5. Sets as lists

    We noted that member(X,[a,b,a,a,a,c,a,b,b]) would have solutions a,b,a,a,a,c,a,b,b.Sometimes we only wish to obtain unique solutions eg a,b,c in this case. A usefulbuilt-in(system) predicate for this issetof. The query

    setof(X,member(X,[a,b,a,a,a,c,a,b,b]),L)

    would be satisfied with L=[a,b,c]. Thussetofhas 3 arguments, with the second being apredicate expression. Similarly

    |?-setof(X,deepmember(X,[[a,b],[[c,[d]],e]),L).

    L = [[a,b],a,b,[[c,[d]],e],[c,[d]],c,[d],d,e]

    |?-setof(X,deepatommember(X,[[a,b],[[c,[d]],e]),L).

    L = [a,b,c,d,e]

    |?-setof(X,deepatommember(X,[[a,b],[[c,[b]],a]),L).

    L = [a,b,c]

    |?-setof([L,M],concatenate(L,M,[a,b,c]),K).

    K = [[[],[a,b,c]],[[a],[b,c]],[[a,b],[c]],[[a,b,c],[]]].

    It should be noted that the "result" is still represented as a list however. Sometimes we dowant all values that satisfy some predicate, duplicates included. Thebagofbuilt-in predicatedoes just this:

    |?-bagof(X,deepatommember(X,[[a,b],[[c,[b]],a]),L).

    L = [a,b,c,b,a]

    2.6. Mixing List Predicates

    In defining predicates we can use other predicates. Thus

    subset([],L).

    subset([X|M],N) :-member(X,N),subset(M,N).

    though this doesn't test whether things are actually sets rather than lists.

    Below we give two versions of a reversing predicate, the first of which is simple but slow,and the second of which makes use of another predicate with extra parameter.

    naiveReverse([],[]).

    naiveReverse([X|L],M) :-naiveReverse(L,N),concatenate(N,[X],M).

    reverse(L,M) :-reverseD(L,[],M).

    reverseD([],Y,Y).

    reverseD([X|L],Y,M) :-reverseD(L,[X|Y],M).

    2.7. Practical 2

    1) Write a predicateflattenso thatflatten(X,Y) is true when Y is the list containing allthe atomic elements of X in the sense thatflatten([[a,b],[[c,[d]],e]],[a,b,c,d,e]) is true. Whathappens to the queryflatten(X,[a,b,c])?

    2) Write a predicatesubstwith four arguments so thatsubst(x,y,[x,[z,x]],M) is satisfied

    with M=[y,[z,y]]. What happens with the querysubst(x,y,M,[y,[z,y]])? Withsubst(M,N,[x,

    18

  • 8/12/2019 Prolog Tutor

    19/39

    [z,x]],[y,[z,y]])? What other uses can be made of such a predicate? Modify the predicate sothatsubst(x,y,x,M) is also satisfied with M=y.

    3) Write a predicatedeletewith 3 arguments so thatdelete(X,Y,Z) is true when Z is thesame as Y with all occurences of X removed, egdelete(a,[x,[a,b,[a]],c],[x,[b,[]],c]) is true.Again investigate what happens with various combinations of the arguments bound andunbound.

    4) Write a predicatesubsequencewith two list arguments which is true when theelements in the first list occur in that order in the second list egsubsequence([a,b,c],[d,a,x,y,b,g,c]) is true.

    5) Write a predicatedenilwith two arguments so thatdenil([a,[[],b,[[]]],c],M) issatisfied with M=[a,[b],c].

    7) Write a predicatezippedwith three arguments so thatzipped([a,b,c],[x,y,z],M) issatisfied with M=[[a,x],[b,y],[c,z]]. Get it to do something sensible if the first two argumentsare of different length. What happens if the arguments in the query are notbound,bound,unbound in that order - experiment a bit?

    8) Write a predicatetailFromwith three arguments, so thattailFrom(A,Xs,Ys) is truewhen Ys is the list starting from the first occurrence of A in Xs, containing A and all the

    subsequent elements, egtailFrom(a,[b,c,a,c,a,d],[a,c,a,d]) is true, andtailFrom(a,[b,c,a,c,a,d],[a,d]) is false.

    9) Try a few more examples from the Sicstus Prolog Manual Programming Examples.

    19

  • 8/12/2019 Prolog Tutor

    20/39

    3. Week 3

    3.1. Using Structures

    3.1.1. Structures as trees

    The data structures are the terms used in logic. These are implemnted with recursive datastructures (trees). The facts we used in earlier examples were fairly simple ones such as

    father(fred,james). Note that a list [X|L] is regarded as a dotted pair ie in predicate form .(X,L), and hence that a list such as [a,b,c] would be .(a,.(b,.(c.[]))).

    An example from Bratko (another not so good book on Prolog and AI) gives informationabout a family in the form

    family(person(tom,fox,date(7,may,1950),works(bbc,15200)),

    person(ann,fox,date(9,may,1951),works(ici,17500)),

    [ person(pat,fox,date(5,may,1973),unemployed),

    person(jim,fox,date(5,may,1973),unemployed) ] ).

    If each predicate name becomes the root of a subtree, then the whole structure can bethought of as having the following tree structure.

    family

    person .

    tom fox date works

    person

    annefox date

    " may#$%& bbc#%'&& $ may#$%#

    works

    ici #"%&&

    person

    pat fox date

    % may#$"(

    )nemployed

    .

    person

    jim fox date

    % may#$"(

    *+

    )nemployed

    3.1.2. Anonymous Terms

    Sometimes we wish to find the values of only some arguments of a predicate, or generalstructure, ie we don't care what values the other arguments have. To indicate this we use theanonymous variable _ rather than find another name. This anonymous variable matchesanything, and if more than one appears in an expression they may match different values.Assume that we have a database of facts about families such as the above, and add thefollowing rules:

    husband(X) :-family(X,_,_).

    wife(X) :-family(_,X,_).

    child(X) :-family(_,_,Children),member(X,Children).

    20

  • 8/12/2019 Prolog Tutor

    21/39

    alive(X) :-husband(X)

    alive(X) :-wife(X)

    alive(X) :-child(X).

    dateofbirth(person(_,_,Date,_),Date).

    salary(person(_,_,_,works(_,S)),S).

    salary(person(_,_,_,unemployed,0).

    When queries are entered the system tries to match structures, with _ matching anything.Thus the query wife(person(Name,fox,_,Job)) would try to satisfyfamily(_,person(Name,fox,_,Job),_). This structure looks like

    family

    , person

    name fox job

    ,

    ,

    and thus is matched with Name=ann and Job=works(ici,17500). To get the names of allpeople in the database we could do

    |?-alive(person(N,M,_,_)).

    which would return solutions one at a time. To get all children born in 1981 we would do

    |?-child(X),dateofbirth(X,date(_,_,1981)).All unemployed people born before 1963

    |?-alive(person(N,M,date(_,_,Y),unemployed)),Y

  • 8/12/2019 Prolog Tutor

    22/39

    and the following Prolog session

    |?-setof(X,likes(X,Y),L).

    X = _0

    Y = beer

    L = [dick,harry,tom] ;

    X = _0Y = whisky

    L = [april,june,may] ;

    no

    |?-setof(X,likes(X,_),L).

    X = _0

    L = [dick,harry,tom] ;

    X = _0

    L = [april,june,may] ;

    Ignore the X= lines as they are irrelevant. Note that the first query satisfies thesetoftwice,once for each value of Y. Thesetofquery with anonymous variable is equivalent to this - itjust doesn't print the Y= line. The way to find the set of all people who like something is asfollows

    |?-setof(X,Y likes(X,Y),L).

    X = _0

    Y = _1

    L = [april,dick,harry,june,may,tom] ;

    If you wanted the set of all Ys, each with the corresponding set of Xs you would dosomething like

    |?-setof((Y,S),setof(X,likes(X,Y),S),SS).

    Y = _0

    S = _1

    X = _6

    SS = [(beer,[dick,harry,tom]),(whisky,[april,june,may])] ;

    Thus to get a list of name,surname pairs for Bratko's family example we do

    |?-setof([N,M],X Y alive(person(N,M,X,Y)),L).

    which is to be read as "L is the list of all [N,M] such that there exists X,Y withalive(person(N,M,X,Y))" where implicitly the X,Y may depend on N,M. The functor isonly used insidesetofand bagof.

    3.2. Operator notation

    In ordinary arithmetic we use infix notation and operator precedence to understand that2+3*4 means (2+(3*4)) ie 14 rather than (2+3)*4 ie 20. We also know that 7-4-2 means (7-4)-2rather than 7-(4-2) ie we work left to right for operators of equal precedence. Prologarithmetic follows this standard usage. Such expressions should be thought of as structuresor trees, eg 2+3*4 is +(2,*(3,4)) ie

    22

  • 8/12/2019 Prolog Tutor

    23/39

    +

    2 *

    3 4

    Thus we wish 7-4-2 to stand for the structure -(-(7,4),2) ie the tree

    -

    2-

    7 4

    Atoms always have precedence 0, and a smaller number denotes higher precedence.Operators must be defined to be one of the following forms.

    infix operators of three types: xfx, xfy, yfx

    prefix operators of two types: fx, fy

    postfix operators of two types: xf, yf

    To achieve the appropriate calculations the arithmetic operators in Prolog have theprecedence etc of

    :-op(700,xfx,[=,is, etc]).

    :-op(500,yfx,[+,-]).

    :-op(500,fx,[+,-]).

    :-op(400,yfx,[*,/]).

    With these definitions -6/3-4*7+12 is calculated as (((-(6/3))-(4*7))+12) and has the structure+(-(-(/(6,3)),*(4,7)),12). This has tree form

    23

  • 8/12/2019 Prolog Tutor

    24/39

    +

    12-

    -

    4/

    3

    *

    6

    7

    Within an expression the principal functor is the operator with largest precedence eg '+' inthe above. The operators with the smallest value bind the strongest eg '*' in the above. In aspecifier the x indicates that the argument on that side must have a lower precedence thanthe operator, and the y indicates that the argument must have precedence less than or equalthan the operator. Thus withop(500,yfx,'-') the first argument of '-' must have a precedenceno more than 500 and the second argument must have precedence less than 500. The treeabove corresponds to the precedence structure:

    y500x

    0y500x

    y500x y400x

    00

    00

    y400x

    Precendence can be applied to predicates as well as functors. Consider the followingprogram

    :-op(200,xfy,and).

    :-op(150,fx,the).

    24

  • 8/12/2019 Prolog Tutor

    25/39

    :-op(300,xfx,supports).

    the tablesupportsthe vase.

    the floorsupportsthe chair and the table and the lamp.

    the chairsupportsfred.

    The structures corresponding to these last three statements are

    supports(the(table),the(vase))supports(the(floor),and(the(chair),and(the(table),the(lamp))))

    supports(the(chair),fred)

    We can now queries like

    |?- What supports fred.

    What = the chair

    |?- the floor supports What.

    What = the chair and table and the lamp

    3.3. Arithmetic

    3.3.1. Introduction

    While natural number arithmetic can be done with the Peano term representation, for large

    numbers it is inefficient. Prolog provides built-ins to handle arithmentic in the usual

    postional representation.

    3.3.2. Comparisons

    Numbers can be compared with built-in comparison operators. Thus we could define apredicatepositiveby:

    positive(X) :- X>0.so thatpositive(3) is true, andpositive(-9) is false. Our previous comments and exerciseshave led us to ask what happens if we use the querypositive(X) with X unbound - maybe itwill generate all X such that the query is true. But no. Now we have problems, because somePrologs give an error message and some like Sicstus just sayno.

    The buit-in arithmetic predicates are normally used in infix form. They include>,=and=

  • 8/12/2019 Prolog Tutor

    26/39

    in which case Y is unified with the expression 2+3 and not its value. To unify it with thevalue we use the buit-in predicateis:

    |?- Yis2+3.

    Y = 5

    We can have general arithmetic expressions on the right hand side of theis. Note thatisisalso a predicate of which is satisfied when the left hand side is equal to the value of the right

    hand side, ie the first thing it does is try to evaluate the right hand side, which musttherefore be evaluable in arithmetic. Thus

    |?- Yis3+2*8.

    Y = 19.

    |?- 3+2isX.

    no

    We can use arithmetic in other predicates eg:

    len([],0).

    len([X|L],Y) :-len(L,N), YisN+1.

    addEm([],0).

    addEm([X|L],M) :-addem(L,N), MisX+N.

    max(X,Y,Z) :- X>=Y, ZisX.

    max(X,Y,Z) :- X

  • 8/12/2019 Prolog Tutor

    27/39

    T = rectangle(3,5)

    |?- Z=..[p,X,f(X,Y)]

    Z = p(X,f(X,Y))

    This is useful when we have such structures as representations for data objects in a program.It is also useful for writing general purpose predicates for altering facts about differentstructures. Consider a program which contains information about geometric objects with

    predicates square(Side), rectangle(Side1,Side2), circle(R). Suppose we wish to have a generalpurpose enlargement predicate to increase the size of any object, so we need to be able tomultiply the single argument of a square or circle by some appropriate factor F, but alsomultiply both arguments of a rectangle etc. Thus given any shape Type(X1,...,Xn) we need tobe able to multiply all of X1,...Xn and have this work for any such Type. We want to be ableto say enlarge(Type(X1,...,Xn),F,Type(Y1,...Yn)), but we can neither have such a dotted list inProlog (of indefinite length), nor can the predicate name Type be a variable. Instead we use=.. as follows:

    enlarge(Fig,F,Fig1) :- Fig =.. [T|P],multiplyList(P,F,P1), Fig1=..[T|P1].

    multiplyList([],_,[]).

    multiplyList([X|L],F,[Y|M]) :- Y

    isX*F,

    multiplyList(L,F,M).

    ie we first convert Fig to a list, do the multiplication across the members of the listcorresponding to the parameters, and then convert back to a structure. We could then sayenlarge(circle(X),3,circle(Y)) if X were already bound, and Y would be bound to 3*X, orenlarge(Fred,Scale,Freda) if Fred were known to be a figure of some sort and Freda was to bethe rescaled version.

    Univ is also useful if we need to create higher level predicates, or otherwise manipulatepredicates before satisfying them. We may wish to applypositiveto all members of a list inone situation and applynegativeto them in another. We could program these separately by:

    allpositive([]).

    allpositive([X|L]) :-positive(X),allpositive(L).

    allnegative([]).

    allnegative([X|L]) :-negative(X),allnegative(L).

    but these predicate definitions have the same form. In each case we are running a singleargument predicate across the list until it fails. It is possible to define

    allpositive(L) :-mapList(L,positive).

    allnegative(L) :-mapList(L,negative).

    withappliesToEachsuitably defined. This can be done as follows:

    mapList([],Pred).

    mapList([X|L],Pred) :- Goal=..[Pred,X],Goal,mapList(L,Pred).

    If we then saymapList([2,4,3],positive), Goal is first set topositive(2) by the univ, this goal istested and if true Fun is applied to the rest of the list. In some versions of Prolog the simple"Goal," would have to be replaced with the built-incall(Goal).

    3.4.2. Extracting parts of structures.

    Given a structure f(X,Y,...N) the f is referred to as the functor and the X,Y,...N as thearguments. The following two predicates are provided:functor(Term,F,N) is true if F is theprincipal functor of Term and N is itsarityie number of arguments,arg(N,Term,A) is true ifA is the Nth argument of Term. Thus:

    |?-functor(t(f(X),X,t),Fun,Arity).

    Fun = t

    Arity = 3

    27

  • 8/12/2019 Prolog Tutor

    28/39

    |?-arg(2,f(X,t(a),t(b)),Y).

    Y = t(a)

    |?-functor(D,date,3),arg(1,D,29),arg(2,D,june),arg(3,D,1982).

    D = date(29,june,1982)

    Functor will only work with the following combinations of bound and unbound arguments:B,U,U & U,B,B & B,B,B. This never causes any problems.

    3.5. Decomposing and creating atoms.

    The predicates functor, arg & =.. allow us to manipulate compound terms ie structures.Similarly several modern languages provide facilities to explode or implode atomic names -ie to go between an atomic name and its individual characters. In Prolog only one predicateis provided but it can be used either way. Note that rather than dealing with individualcharacters it deals with their ascii code

    |?- name(fred,L).

    L = [102,114,101,100]

    |?-name(G,[98,97,96]).

    G = ba

    3.6. Practical 3

    1) Write a predicatetimesEmwhich multiplies a list of numbers.

    2) Usevarandnonvarto make thereversepredicate work both ways, ie with thearguments bound,unbound & also unbound,bound.

    3) Consider the following program:

    :-op(300,xfx,plays).

    :-op(200,xfy,and).

    jimmyplaysfootball and squash.susanplayssquash and tennis and basketball.

    joplaystennis.

    Define new operatorsplaysOnlyso that XplaysOnlyY is true when person X plays onlyone game,playsAllOfso that XplaysAllOfY is true when Y contains more than one game.Define an operatorisOneOfso that XisOneOfY is true is Y is either a single game of a listof games separated withands and X is either that single game or one of the listof games.Define a new operatorplaysTheGameso that X playsTheGame Y is true when Y is one ofthe games (or the only game) that X plays.

    4) Suggest appropriate definitions of the operators 'was', 'of', 'the', 'and' to be able towrite clauses like "pamwasthe secretary and the lifeblood of the department". Test thesewith various sentences and queries.

    5) DefineappliesToEach2with three arguments so that theappliesToEach2(L,Fun,M)is true ifFun(X,Y) is true for X,Y in the corresponding positions in L,M for all such positionsegappliesToEach2([2,6,4,5],add1,[3,7,5,6]) is true where add1 has the obvious meaning.

    6) Define the predicateadd1swith two arguments so thatadd1s(Xs,Ys) is true when Ysis the list of numbers corresponding to the list Xs of numbers but with 1 added to each ofthem, egadd1s([1,4],[2,5]) is true.

    7) Define the predicatepositionswith three arguments so thatpositions(A,Xs,Is) istrue when Is is the list of positions of A in the list Xs, egpositions(a,[b,a,a,b],[2,3]) is true.

    8) Define the predicatefirstPositionwith three arguments so thatfirstPosition(A,Xs,I)

    is true when I is the first position of A in the list Xs, egfirstPosition(a,[b,a,a,b],2) is true.

    28

  • 8/12/2019 Prolog Tutor

    29/39

    9) Define the predicatetakeswith three arguments so thattakes(N,Xs,Ys) is true whenYs is the list of the first N elements in the list Xs, egtakes(3,[b,a,a,b],[b,a,a]) is true.

    10) Define the predicatedropswith three arguments so thatdrops(N,Xs,Ys) is true whenYs is the list of all elements in the list Xs, except the first N, egdrops(3,[b,a,c,d],[d]) is true.

    11) Define the predicatetakesWhilewith three arguments so thattakesWhile(P,Xs,Ys)is true when Ys is the list of initial elements from the list Xs for which P(X) is true, eg

    takesWhile(even,[4,6,3,4],[4,6]) is true.12) Define the predicatedropsWhilewith three arguments so thatdropsWhile(P,Xs,Ys)is true when Ys is the list of elements from the list Xs omitting the initial elements for whichP(X) is true, egdropsWhile(even,[4,6,3,4],[3,4]) is true.

    29

  • 8/12/2019 Prolog Tutor

    30/39

    4. Week 4

    4.1. Types of equality

    Earlier in the notes we introduced the matching equality operator=where E1=E2 was truewhen the variables in E1 and E2 could be matched to unify the two expressions eg:

    |?- fred(a,X,g(Y))=fred(Z,b,g(ferd(c))).

    X = b

    Y = ferd(c)

    Z = a

    |?- X=2+3.

    X = 2+3

    We noted that with arithmetic expressions the arithmetic expression was not evaluated. Toset X equal to the value of 2+3 we would have to do

    |?- Xis2+3.

    X = 5

    so this is assignment rather than an equality check. To do equality checks on the value ofarithmetic expressions we use =:= for equality and =\= for inequality. Thus:

    |?- 5+2=:=10-3.

    yes

    |?- 5+2=\=9-3.

    yes

    Sometimes we want to test the literal equality of compound terms, without doing anyunification. This is done using == for equality and \== for inequality. Thus:

    |?- f(a,b)==f(a,b).

    yes

    |?- f(a,X)==f(a,b).

    no

    |?- f(a,X)==f(a,Y).

    no

    |?- X==Y.

    no

    |?- fred(X,jo(b,Y))==fred(X,jo(b,Y)).

    X = _24

    Y = _58

    yes

    |?- X\==Y.

    X = _10

    Y = _34

    yes

    where in the last two cases, the system is essentially saying - yes I can prove that usinganonymous variables.

    30

  • 8/12/2019 Prolog Tutor

    31/39

    4.2. Input

    Primitive character based input is available, but most input is done usingreadwhich expectsa Prolog term to be entered on the current input channel, and instantiates the singleargument of read to this.

    |?-read(X).

    |: no.

    X = no

    yes

    |?-read(X), Y=..X.

    |: [a,b,c,d].

    X = [a,b,c,d]

    Y = a(b,c,d)

    Note that the input prompt is "|: ". Note also the period that the user types to indicate theend of the input. Note that the binding is lost when the query finishes.

    4.3. Output

    So far we have relied on the simple output of varible bindings accumulated by theinterpreter to give us our answers. When we have a longer program from which we wish tooutput information at various points, or when we want the output in a better layout, we willuse the printing facilities provided in Prolog.

    There are primitive input and output predicates for single characters. There are also otherhigher level ones. The predicatewritesends to the current output stream its singleargument which must be a Prolog term (ie a constant, a variable or a compound term). Thepredicate nl produces a newline. It is convenient to also have a writeln predicate defined by

    writeln([]) :-nl.

    writeln([X|L]) :-write(X),writeln(L).

    and something to print a list of values with a space between them.

    gapWriteln([]) :-nl.

    gapWriteln([X|L]) :-write(X),write(' '),gapWriteln(L).

    We could use these to neatly print theancestors of someone in the family knowledge base,thus:

    pancestors(X) :-setof(Y,ancestor(X,Y),L),

    writeln(['Theancestors of ',X,' are :']),gapWriteln(L).

    pancestors(X) :-writeln([X,' has no ancestors in the database.']).

    which would be used as follows

    |?-pancestors(flo).

    The ancestors of flo are :

    harry henrietta james jerri peter petra rob rowena

    yes

    |?-pancestors(jerri).

    jerri has no ancestors in the database.

    yes

    Note thatnlandwrite, and hencewritelnandgapWriteln, will always succeed if theirarguments are bound. Two things should be noted about thepancestorspredicate. It willalways succeed - because either ancestors exist in which case thesetofsucceeds or ancestors

    31

  • 8/12/2019 Prolog Tutor

    32/39

    don't exist in which casesetoffails and the other clause is used. The order of the clauses isimportant in that the second is used only when the first has failed.

    4.4. Controlling backtracking - the cut, !

    4.4.1. Introduction

    A programmer may wish to prune the backtracking search tree, for efficiency reasons. Thiscan be done explicitly using the cut,!. A cut in a clause prunes all the clauses below it. A cut

    also prunes all alternative solutions to the conjunction of goals which appear to the left of it.In this way it gives only one solution, the first to be found. Injudicious use of the cut canlead to unsound answers. It should be used sparingly and only when you understandprecisely what it is doing.

    The cut can be usefully used to extend definite clause logic to include a weak form ofnegation. This is best achieved using the in-built meta-predicates.

    4.4.2.Negation as failure

    With the family database, if we wished to check whether james was NOT the father of fred,

    or lucy, we would try

    |?-father(fred,james).

    yes

    |?-father(lucy,james).

    no

    To reverse these answers we could define the meta-predicatenotby

    notP :- P, !, fail.

    notP.

    where fail is a built-in (but doesn't have to be) which always fails. Then

    |?-notfather(fred,james).

    no

    |?-notfather(lucy,james).

    yes

    We might think that this seems correct, as the statement father(fred,james) is indeed false,becausefather(fred,james) is true. The second is given as true. This is because, in ourknowledge base, it is not known whether james is the father of lucy. It may well be thatjames is the father of lucy, but we simply have not recorded the fact. Negation in Prolog issometimes called negation by failure, ienotpis regarded as true if we fail to satisfyp. Thisis sometimes known as the closed world assumption, as the answer is true simply for theclosed world represented by our knowledge base, and does not imply anything about theworld outside. negation is built-in to all Prologs. In Sicstus Prolog it is represented by/+. It

    was used previously in\+X=Y to definedeepmember(X,M).

    4.43.Negation with variables

    \+p(X) will be false ifp(X) can be satisfied. Thus ifp(X) is true for some X,\+p(X) will be

    false and the Prolog interpreter will return the answer no. It is answering the query p(X)

    and not the one you might have intended Xp(X).\+p(X) will be true only ifp(X) is truefor no X. Thus a query\+p(X) will not instantiate X to anything useful.

    Consider the problem of finding all theancestors of fred except his parents. A sufficientquery is

    |?-ancestor(fred,X),\+parent(fred,X).

    This will operate by finding an X satisfyingancestor(fred,X), AND THEN checking whether

    \+parent(fred,X) is true for this X. Thus the system tries to satisfy the predicate\+parent(fred,X) only with X already bound to a value. It repeats this with everyancestorof

    32

  • 8/12/2019 Prolog Tutor

    33/39

    fred, as long as we keep responding with a semicolon. This will work and give the correctanswers.

    Consider the effect of

    |?-\+parent(fred,X) ,ancestor(fred,X).

    This will try to satisfyparent(fred,X) first which succeeds, hence\+parent(fred,X) fails, andhence the whole query fails. In this way Prolog does not allow a strictly logical reading - the

    order of the predicate expressions matters.A general rule for including negations in queries is that all their variables should beinstantiated before they are reached.

    4.4.4. Order of negations inside a query

    In the "negation with variables" example we saw that the order or predicate expressions in aquery had some effect, because the comma separating the predicate expressions has theoperational interpretation of sequencing: "satisfy the firstand thensatisfy the second". In apure query with no negation and no sideeffects (assignment, input and output) and no othermeta-logical features (covered later) the order does NOT matter. Thus, in that case weachieve the same result no matter what order the predicate expressions are in - ie the commahas a strict logical interpretation same as a logicaland.

    4.4.5. If..then..else

    Consider the expressionif p then q(0) else q(1)where p is very complicated, and maybecontains side effects. We get that effect by

    q(X) :-p, !, X=0.

    q(1).

    Ifpis not true thenq(1) succeeds,q(0) fails andq(X) has the single solution X=1. Ifpis truethenq(0) succeeds,q(1) fails andq(X) has the single solution X=0. This can be written usingthe built-inif..then..else:

    q(X) :-p X=0;X=1.

    This in fact uses the infix meta-predicate ';' which exploits the search order to imitatedisjunction. This built-in could be defined by:

    P;Q :- P.

    P;Q :- Q.

    4.5. Knowledge Base manipulation

    Facts and rules in earlier examples were kept in files, where they remained between programruns, and were available to a program throughconsulting those files. During a program runor Prolog session various variables were bound to values during predicate satisfaction, butthe values were lost after the predicate finished, except when passed through parameters.One way to make values remain more permanently would be to write them to files.

    However, sometimes we wish to retain values only for all or part of the remainder of thecurrent session. The way to do this is to use theassertfamily of predicates. This not onlyallows us to store facts such asfather(fred,james) but also rules. It is customary to refer tothe things in the files as the static knowledge base, and those kept only during one session asthe dynamic knowledge base. Note that during a Prolog session the Prolog sessionknowledge base contains the knowledge obtained from the files using consult + the thingsasserted during the session - the things "deleted" during the session. The usual way to"delete" knowledge during a session is using theretractpredicate. Note that this deletesanything in the Prolog session knowledge base whatever its source, but does not deleteentries in the files themselves.

    Consider the following small knowledge base

    nice:-sunny,\+raining.funny:-sunny,raining.

    33

  • 8/12/2019 Prolog Tutor

    34/39

    horrible:-raining,fog.

    raining.

    fog.

    and the following Prolog session.

    |?-nice.

    no|?-horrible.

    yes

    |?-retract(fog).

    yes

    |?-horrible.

    no

    |?-assert(sunny).

    yes

    |?- funny.

    yes

    |?-retract(raining).

    yes

    |?- nice.

    yes

    Here we only asserted and retracted simple facts. A fact such asfather(tony,john) is assertedby assert(father(tony,john)). A rule can be asserted as in the following example:assert((have_same_child(X,Y) :- father(Z,X),mother(Z,Y))). Note that the rule has to be

    wrapped in parentheses. Note the following|?-assert(first([X|L],X)).

    X = _0

    L = _1

    yes

    |?-first([a,b,c,d],Y).

    Y = a

    ie facts with variables in them can be asserted.

    Given the family knowledge baseretract(father(X,james)) would look for facts matching

    father(X,james) and progressively retract them until told to stop, thus:|?-retract(father(X,james)).

    X = fred ;

    X = flo

    yes

    |?-retract(father(Y,rex)).

    Y = jill

    yes

    Note that assert puts the information somewhere in the knowledge base - but we are not

    guaranteed anything about where the information actually is. The two predicates asserta,

    34

  • 8/12/2019 Prolog Tutor

    35/39

    assertz specifically put the new knowledge at the start and end of the knowledge baserespectively. Consider the following definition of a predicate to calculate fibonacci numbers

    fib(1,1).

    fib(2,1).

    fib(N,F) :- N1isN-1, N2isN-2,fib(N1,F1),fib(N2,F2), FisF1+F2.

    Consider the following partial trace:

    (1) 1 Call:fib(5,_0) ?

    (4) 2 Call:fib(4,_6) ?

    (7) 3 Call:fib(3,_13) ?

    (10) 4 Call:fib(2,_20) ?

    (10) 4 Exit:fib(2,1)

    (11) 4 Call:fib(1,_21) ?

    (11) 4 Exit:fib(1,1)

    (7) 3 Exit:fib(3,2)

    (13) 3 Call:fib(2,_14) ? (13) 3 Exit:fib(2,1)

    (4) 2 Exit:fib(4,3)

    (15) 2 Call:fib(3,_7) ?

    (18) 3 Call:fib(2,_31) ?

    (18) 3 Exit:fib(2,1)

    (19) 3 Call:fib(1,_32) ?

    (19) 3 Exit:fib(1,1)

    (15) 2 Exit:fib(3,2)

    (1) 1 Exit:fib(5,5)

    F = 5

    yes

    Thus ,it has done all the calculations forfib(3) twice. The facts forfib(2) andfib(1) it onlyhad to extract from the knowledge base. The solution is to assert calculated information intothe knowledge base, by changing the last clause of the definition offibto

    fib(N,F) :- N1isN-1, N2isN-2,fib(N1,F1),fib(N2,F2),FisF1+F2,asserta(fib(N,F)).

    Now oncefib(N,F) has been calculated,fib(M,F) for all M between 1 and N is stored beforethe recursive rule in the knowledge base. Hence

    |?-fib(7,H).H = 13

    yes

    |?- trace.

    yes

    |?-fib(5,J).

    (1) 1 Call:fib(5,_0) ?

    (1) 1 Exit:fib(5,5)

    J = 5

    The predicateabolishretracts all facts and rules for a particular predicate. Thusabolish(fib,2) would remove not onlyfib(1,1),fib(2,1) but also thefib(N,F) rule. The second

    35

  • 8/12/2019 Prolog Tutor

    36/39

    argument of abolish is the arity of the predicate to be abolished. This is useful when we wishto run a program several times during a Prolog session, as at any stage, we can simplyabolish facts obtained in previous runs.

    36

  • 8/12/2019 Prolog Tutor

    37/39

    4.5.3. Example with Disjointness.

    Consider the predicatef(X,Y) which has Y=0 for X6. Wecould define this by

    f(X,0) :- X

  • 8/12/2019 Prolog Tutor

    38/39

    oncedeleteone(X,[X|L],L) :- !.

    oncedeleteone(X,[Y|L],[Y|M]) :-oncedeleteone(X,L,M).

    which behaves like

    |?-oncedeleteone(a,[a,b,c,a,a,c,a],L).

    L = [b,c,a,a,c,a] ;

    noSuch cuts are sometimes added for efficiency reasons. This could be written using theas

    oncedeleteone(X,[Y|L],N) :- X=Y N=L ;oncedeleteone(X,L,M),N=[Y|M].

    I indicated above that some provided predicates could be defined using the cut. These oftenmake use of two special zero-argument predicates: true & fail. The first of these is alwaystrue and the second always false.

    Code which contains cuts is frequently bad code. Avoid them wherever possible. If theymust be used for some reason, remember that it is very easy to write such code incorrectly. I

    tend to use theinstead, but not very often. My predicates which contain !s ors tend tobe very short with most of the work being done in other predicates without any cuts orif..then..elses.

    38

  • 8/12/2019 Prolog Tutor

    39/39

    4.7. Practical 4

    1) Write a minimum functionmin(X,Y,Min) without any cuts orif..then..elseRewrite

    it using. Rewrite it using cuts.

    2) Extend the weather example as follows. Start with

    weather(X) :-fact(X,yes).

    fact(nice,yes) :-fact(sunny,yes),fact(raining,no).

    fact(funny,yes) :-fact(sunny,yes),fact(raining,yes).

    fact(horrible,yes) :-fact(raining,yes),fact(fog,no).

    fact(X,no) :-\+fact(X,yes).

    symptoms([raining,fog,sunny]).

    Write a predicateaskabout(X) which asks whether X is yes or no, and asserts that fact,retracting previous assertions about that X first. Write a predicateaskall(L) which asksabout all elements in L. Write a predicateinitthat does askall on the list of symptoms. Testyour predicates. Extend its knowledge about weather a bit.

    3) Load the wumpus1.pl simulator from the code repository.

    Initialise the simulator with the query:

    |?-initialize([Stench,Breeze,Glitter,no,no]).

    This positions the wumpus and the pits as in Figure 6.2 of Russel and Norvig.

    Now, behave as the agent yourself using asequence of queries of the form

    |?-execute(Action,[Stench,Breeze,Glitter,Bump,Scream])

    where Action one of:

    % goforward: move one square along current orientation if possible

    % turnleft: turn left 90 degrees% turnright: turn right 90 degrees

    % grab: pickup gold if in square

    % shoot: shoot an arrow along orientation, killing wumpus if in that direction

    % climb: if in square 1,1, leave the cave and adds 1000 points for each piece of gold.

    Write code for a pig-headed agent which ignores the percepts and carries out a prescribedsequence of actions.

    39