CSCI 431: Logic Programming

(This presentation is taken primarily from an Introduction to Prolog by Paul Brna and Tamsin Treasure-Jones at the University of Leeds http://cbl.leeds.ac.uk/~tamsin/prologtutorial. Also check out Paul Brna's on-line book at http://cblslca.leeds.ac.uk/~paul/prologbook.)

What is Prolog?

Prolog as a Programming Language

So, what's a declarative language?

What does a declarative program look like?

And what's a relational program?

A little history

Simple Facts

Examples of Simple Facts

Facts with Arguments

Examples of Facts with Arguments

More Examples of Facts with Arguments

Variables and Unification

Variable Examples

More Examples of Variables

Rules

Rules Part 2:

Rules Part 3

Rules Part 4

Rules Part 5

Examples of Rules

More Examples of Rules

Introducing Backtracking

Backtracking in Rules

Search Example Part 1

Search Example Part 2

Search Example Part 3

Search Example Part 4

Search Example Part 5

Lists

List Examples 1

List Example 2


Backtracking and Cut

(Taken from material written by James Power)

Prolog differs from imperative languages (like C) in that it concentrates on dealing with facts and rules, rather than sequences of instructions. However, for effificency, it can sometimes be desirable to add explicit control information to programs - this is the purpose of the cut.

Analysing Cases

Suppose you were asked to write a Prolog program that would take in someone's exam mark and work out their grade. It might look something like the following:
  grade(N,first) :- N>=70.
  grade(N,two_1) :- N<70, N>=63.
  grade(N,two_2) :- N<63, N>=55.
  grade(N,third) :- N<55, N>=50.
  grade(N,pass) :- N<50, N>=40.
  grade(N,fail) :- N<40.
While this will work it is a little inefficient. The query grade(75,G) will answer G=first as expected but, once this has been satisfied, Prolog will go back to look for any other solutions. In order to do this it will process all of the other options, failing during the body of the rule in each case.

If we were implementing this in C, we might try using a "switch" statement as follows:

  // This code is a little artifical for the purpose of comparison

  int fir(int n) { return n>=70; }
  int fir(int n) { return n<70 && n>=63; }
  //  ... fill in the rest ...
  int fai(int n) { return n<40; }

  switch(n) {
    case(fir(n)): cout << "1st"; break;     
    case(tw1(n)): cout << "2.1"; break;     
    case(tw2(n)): cout << "2.2"; break;     
    case(thi(n)): cout << "3rd"; break;     
    case(pas(n)): cout << "Pass"; break;     
    case(fai(n)): cout << "Fail";
  }
Here we explicitly indicate that after one result has been accepted, we need not look at any of the others at all - this is the purpose of the "break" statement in each branch.

We can do something similar in Prolog to improve efficiency. Basically, we want to tell Prolog that once it has satisfied one version of the predicate, it need look at no other. Prolog's equivalent of the break statement here is the cut, written "!".

To elimiate useless backtracking from the above, (and taking advantage of Prolog's order of execution) we can rephrase the program as:

  grade(N,first) :- N>=70, ! .
  grade(N,two_1) :- N>=63, ! .
  grade(N,two_2) :- N>=55, ! .
  grade(N,third) :- N>=50, ! .
  grade(N,pass) :- N>=40, ! .
  grade(N,fail) :- N<40.
The cut predicate has the effect of telling Prolog not to pass back through this point when it is looking for alternative solutions. Thus, the ``!'' acts as a marker, back beyond which Prolog will not go. When it passes this point all choices that is has made so far are ``set''; ie. they are treated as though they were the only possible choices.

Note that the cut always appears where a predicate can appear (never, for example, as arguments to a predicate). It is treated at this level just like any other predicate, and it alwayssucceeds.

In summary, the effect of the cut is as follows:

  1. Any variables which are bound to values at this point cannot take on other values
  2. No other versions of predicates called before the cut will be considered
  3. No other subsequent versions of the predicate at the head of the current rule will be considered
  4. The cut always succeeds.
Basically, any more answers to the current query must come from backtracking between the point of the cut and the end of the current rule.

An Example Of Using The Cut

Save the following knowledge base in a file, and read it into Prolog:
  holiday(friday,may1).

  weather(friday,fair).
  weather(saturday,fair).
  weather(sunday,fair).

  weekend(saturday).
  weekend(sunday).

  % We go for picnics on good weekends and May 1st
  picnic(Day) :- weather(Day,fair), weekend(Day).
  picnic(Day) :- holiday(Day,may1).
Pose the query:
  picnic(When).
You should get three answers; make sure you understand where they come from! Note that in order to get this answer, Prolog had to work through exactly one unsuccessful instantiation of When with "friday", before getting it right the second time.

The Cut

Now change the definition of picnic to the following:
  picnic(Day) :- weather(Day,fair), !, weekend(Day).
  picnic(Day) :- holiday(Day,may1).
Now when we pose the query: Picnic(When) Prolog will try to satisfy the sub-goal:
  weather(When,fair), !, weekend(When).
The first rule for weather is:
  weather(friday,fair), 
so the new sub-goal becomes:
  ....., !, weekend(friday).
Prolog passes the cut, and goes on to try to satisfy
  weekend(friday)
which fails. Previously, it would have backtracked to the last choice point, and gone on with processing
  weather(saturday,fair)
But now the presence of the cut stops it going back, so it is trapped between the cut and the end of the (failed) predicate.

The answer now is simply:

  No.