Copyright © 2008 David Schmidt

Chapter 7:
Introduction to Prolog


7.1 Databases and searches for witnesses
7.2 Horn clauses and backwards chaining
7.3 Data structures and unification
    7.3.1 List data structures
    7.3.2 A control structure: findall
7.4 Example: Change Making
7.5 Example: Sorting
7.6 A few additional useful Prolog predicates
7.7 An implementation you can use
7.8 Conclusion


In the previous chapter, we saw how predicate-logic propositions could be reformatted with Skolem functions into clause form, where the FORALL and EXIST quantifiers are no longer written.

Prolog (``PROgramming in LOGic'') is a programming language based on the predicate calculus, reformatted in such a style. More precisely stated, Prolog is the predicate calculus restricted to FORALL, -->, ^ and a limited use of EXIST. Prolog programs are literally propositions from predicate logic, and the execution of the programs are literally proofs built using the FORALLe, -->e, ^i (and limited use of) EXISTi laws.

Here is a quick example. Recall the classic example: ``all humans are mortal; Socrates is human; therefore, Socrates is mortal.'' Here is how the first two clauses are written in predicate logic:

FORALLX(human(X) --> mortal(X))
human(socrates)
The two propositions look like this when written as Prolog code:
mortal(X) :- human(X).
human(socrates).
(The FORALL is deleted, and --> is ``flipped'' into :-.)

The Prolog interpreter (theorem prover) reads and saves the above program. We ask the interpreter to prove `` Socrates is mortal'' like this:

?- mortal(socrates).
The prover builds the proof (using the resolution technique from the last chapter) and prints
true
meaning that it found a proof.

We can even ask the Prolog interpreter to discover who it is that is mortal, like this:

?- mortal(Z).
(That is, we ask to prove the proposition, EXISTZ mortal(Z)). The interpreter replies with
Z = socrates
showing that it built the proof, using socrates as the ``witness'' for Z.

Prolog was developed in the 1970s as an application of resolution theorem proving. Its ease of use, plus its inclusion of dynamic lists (like the ones in Python), make it a prominent (if not the prominent) language for solving problems in artificial intelligence, deductive databases, and data mining (let alone, theorem proving). A small amount of knowledge about Prolog goes a long way, and this chapter attempts to give you some knowledge.


7.1 Databases and searches for witnesses

A Prolog program consists of a set of premises, the database. A user interacts with the database by submitting queries (goals to be proved). The Prolog interpreter uses predicate logic to prove or to refute the queries.

The simplest form of premise is a primitive proposition, like these two:

expensive(car).
expensive(house).
Read these as asserting ``a car is expensive''; ``a house is expensive.'' We can query the the database, like this:
?- expensive(car).
true

?- expensive(coffee).
false
The first query, ?- expensive(car), is immediately proved by the Prolog interpreter, and the second cannot be proved. (The interpreter examined all the premises in its database, and none matches the query.)

Prolog's power comes from this form of query:

?- expensive(X)
X = car.
This query asks, ``is there something, call it X, that is expensive?'' The interpreter replies with the value for X that makes the query proved true. More precisely stated, the query is really, ``EXISTX expensive(X) ?'' The Prolog interpreter searched and found an individual (car) that proves the goal. (There is an implicit use of the EXISTi-rule here.)

Prolog lets you repeat the same query, searching for multiple, different answers, by typing a semicolon, like this:

?- expensive(X).
X = car ;
X = house ;
false.
That is, the user typed, ?- expensive(X), the interpreter replied, X = car, the user typed ;, the interpreter replied, X = house, the user typed ;, the interpreter replied with false (no more).

Here is a database that uses variables X and Y in its premises. (The variables are implicitly prefixed by FORALLX and FORALLY in the premises.)

===================================================

likes(john, icecream).
likes(john, mary).
likes(X, kim).
likes(ed, Y).

===================================================
The first premise asserts that john likes icecream. The third premise can be read as everyone likes kim. It is more precisely understood as FORALLX likes(X,kim). Variables (words that begin with capital letters) in premises are implicitly universally quantified. They are called logical variables, and they can be assigned values while the Prolog interpreter searches for a proof of a query.

Here are two queries (goals to be proved) for the previous database:

===================================================

?- likes(john,kim).
true

?- likes(X, icecream)
X = john ;
X = ed .

===================================================
The second query should be studied. The interpreter scanned the premises in the database, from top to bottom, in that order. (The order of the premises is crucial to Prolog programming.) The first match sets logical variable X=john. The second match, made with the fourth premise, sets X=ed and Y=icecream. The value of Y is not displayed to the user because it is an internal match and Y does not appear in the original query. Sometimes an answer to a query does not matter:
?- likes(Z,kim)
true ;
Z = ed
Read the first answer, true, as saying that Z can be made equal to any value at all (all atoms, as they are called in Prolog. john, icecream, etc., are all atoms.) This answer was found with the third clause in the database, likes(X, kim) (``everyone/everything likes kim''). The second answer, Z = ed, used a different clause, namely, likes(ed, Y), to determine that ed likes kim.


7.2 Horn clauses and backwards chaining

We can write premises that show how to deduce new facts from the premises in the database. For example, we can deduce that X has Y as a sister if
(i) Y is female;
(ii) Y has father M and mother F;
(iii) X has the same father M and mother F.
We would write this law like this in predicate logic:
FORALLX FORALLY FORALLM FORALLW:
    female(Y)  ^  parents(Y,M,W)  ^  parents(X,M,W)
  -->  sisterOf(X,Y)
Here is how the law is written in Prolog:
sisterOf(X,Y) :- female(Y), parents(Y,M,W), parents(X,M,W).
That is, the four quantifiers, FORALL, are omitted; ^ is written as a comma; and --> is written backwards, as :-. (That is, P --> Q is written Q :- P (read ``Q if P'').)

There is a key reason why the implication is written backwards. Say that we are asked to prove sisterOf(a,b) for some a and b. To do this, we must prove as subgoals all of female(b), parents(a,c,d), and parents(b,c,d), for some values c and d. So, the Prolog coding reads as a tactic for proving sisterOf (using -->e and FORALLe).

Here is the new law we wrote plus a database of primitive propositions:

===================================================

sisterOf(X,Y) :- female(Y), parents(Y,M,W), parents(X,M,W).

parents(bart, homer, marge).
parents(lisa, homer, marge).
parents(maggie, homer, marge).
female(marge).
female(lisa).
female(maggie).

===================================================
Here is a query:
===================================================

?- sisterOf(bart, Z).
Z = lisa ;
Z = maggie ;
false.

===================================================
This shows bart has two sisters.

Here is a summary of the steps taken by the Prolog interpreter to prove Z=lisa:

  1. The goal is sisterOf(bart, Z). This matches the first premise, the implication, where X=bart and Z=Y. Three new subgoals are generated: female(Y), parents(Y,M,W), and parents(X,M,W). The goals will be solved in that order. So far, the variables are assigned as
    Z = Y
    X = bart
    

  2. Subgoal female(Y) is proved by the fifth premise, where Y=marge. We have these variables:
    Z = Y
    X = bart
    Y = marge
    

  3. Now, parents(Y,M,W) is tackled. Since Y=marge, a match must be found for parents(marge,M,W). None is found --- the subgoal fails. To repair the failure, the Prolog interpreter backtracks to the previous subgoal, female(Y), and tries to prove it with a different value for Y:
    Z = Y
    X = bart
    Y = ?
    

    Restarting the earlier step, the sixth line of the database matches female(Y), for Y=lisa. We have these variables and their values:

    Z = Y
    X = bart
    Y = lisa
    

  4. Next, parents(Y,M,W) is reconsidered, where Y=lisa. This is proved for M=homer and F=marge:
    Z = Y
    X = bart
    Y = lisa
    M = homer
    F = marge
    

  5. The last goal to prove is parents(X,M,W), and given the above values of the logical variables, this is proved by line 2 of the database.

    There are no more subgoals to prove, so the original query is proved. Z=lisa prints.

When the query is repeated, the Prolog interpreter resumes its proof search, acting as if Y=lisa had failed, and it searches for yet another, different, solution, working from the goal, female(Y):
Z = Y
X = bart
Y = ?
M = ?
F = ?
The search sets Y=maggie, and the last two goals are reproved, with M=homer and F=marge.

You can ask the Prolog interpreter to generate an execution trace of exactly these searching steps. The way to do so is to trace all the predicate names in which you have interest. In the above example, we would type:

?- trace(sisterOf).
?- trace(parents).
?- trace(female).
Then, when we enter the query,
sisterOf(bart, Z).
We see a printout of the clauses used by the interpreter while it searches for a value for Z:
===================================================

[debug] 9 ?- sisterOf(bart,Z).
 T Call: (7) sisterOf(bart, _G464)
 T Call: (8) female(_G464)
 T Exit: (8) female(marge)
 T Call: (8) parents(marge, _L174, _L175)
 T Fail: (8) parents(marge, _L174, _L175)
 T Redo: (8) female(_G464)
 T Exit: (8) female(lisa)
 T Call: (8) parents(lisa, _L174, _L175)
 T Exit: (8) parents(lisa, homer, marge)
 T Call: (8) parents(bart, homer, marge)
 T Exit: (8) parents(bart, homer, marge)
 T Exit: (7) sisterOf(bart, lisa)
Z = lisa 

===================================================
The trace shows us that the interpreter first tried to use marge as a value for Z, bart's sister, but this failed because the bart and marge cannot be proved to have the same parents. Notice also that the intepreter invented its own internal variables, _G464, _L174, and _L175, while it did its search. These variables are not revealed to the human user.

When we continue the trace by typing a semicolon, we see

===================================================

Z = lisa ;
 T Redo: (8) female(_G464)
 T Exit: (8) female(maggie)
 T Call: (8) parents(maggie, _L174, _L175)
 T Exit: (8) parents(maggie, homer, marge)
 T Call: (8) parents(bart, homer, marge)
 T Exit: (8) parents(bart, homer, marge)
 T Exit: (7) sisterOf(bart, maggie)
Z = maggie

===================================================
Which shows how the interpreter continues as if it failed to find a value for Z the first time.

If we would write the proof for Y=lisa in the laws of predicate logic, it would look like this:

===================================================

Database |- EXISTX sisterOf(bart, X)

1. FORALLX FORALLY FORALLM FORALLW
  (female(Y)  ^  parents(Y,M,W)  ^  parents(X,M,W))
       -->  sisterOf(X,Y)                                premise
2. female(lisa)                                           premise
3. parents(lisa, homer, marge)                            premise
4. parents(bart, homer, marge)                            premise
5. female(lisa)  ^  parents(lisa, homer, marge)
       ^ parents(bart, homer, marge)                     ^i (2 times)
6. (female(lisa)  ^  parents(lisa,homer,marge) 
        ^  parents(bart,homer,marge))
   -->  sisterOf(bart,lisa)                            FORALLe (4 times)
7. sisterOf(bart,lisa)                                    -->e 6,5
8. EXISTY sisterOf(bart,Y)                                 EXISTi 7

===================================================
The key is finding the appropriate atoms for the variables, X, Y, M, and F. Prolog does this by exhaustive search of the database, backtracking as needed until a useful combination is found.

The backwards implication is called a Horn clause, and the Prolog's use of backwards implications to generate subgoals that are eventually all proved is called backwards chaining.

The previous definition of sisterOf is a bit imprecise, because it allows the Prolog interpreter to prove that every female with parents is a sister of herself. To remove this faulty conclusion, we can add a not-equals requirement, \=, like this:

sisterOf(X,Y) :- female(Y), parents(Y,M,W), parents(X,M,W), X \= Y.
The subgoal, X \= Y, is proved true if X and Y have distinct, nonequal values. You can also use = to check for equality.

Summary

Prolog is a clever adaptation of predicate logic and resolution theorem proving to programming. The correspondence goes like this:
logic premises == Prolog program code
theorem prover == Prolog interpreter
queries (goals to prove) == input data
witnesses in EXISTi proofs == output answers
In this way, we ``program in logic.'' Prolog works well at coding any ``knowledge base'' as a set of clauses. But indeed, isn't any program nothing more than a ``knowledge base'' mixed together with a control structure for ``searching'' the base? We will see examples later in the chapter that suggest this is so.


7.3 Data structures and unification

Prolog can handle data structures that are based on logical function symbols. For example, in arithmetic, when we write 3+2, the + is a function symbol. Prolog lets us invent function symbols useful to our subject area. (The function symbols, called functors, are actually the Skolem functions we saw in the previous chapter!)

For example, say that we work in a library and we will program a Prolog database of the library's holdings. Perhaps the library stocks books and dvds, so the items can be portrayed as data structure-values like these:

book('David Copperfield', 'Charles Dickens')

dvd('Tale of Two Cities')
The first is a book data structure that holds two data values: the book's title and its author. (Prolog allows strings to be atoms.) The second data structure is a dvd structure that holds the dvd's title. The data-structure-builder names, book and dvd, are called functors because they look like function calls (but they are not).

If the library owns a copy of each of these items, we can write a predicate that asserts these facts:

owns(k0, book('David Copperfield', 'Charles Dickens')).

owns(k3, dvd('Tale of Two Cities')).
The predicate, owns, lists the key (id number) and the item. So, the library owns item k0, the book David Copperfield by Charles Dickens. Here is a summary of the data structures we will use to define the library's database:
ITEM ::=  book(TITLE, AUTHOR)  |  dvd(TITLE)
                where TITLE and AUTHOR are strings
We will use these two predicates:
PRED ::=  owns(KEY, ITEM)  |  borrowed(KEY, PERSON, DUEDATE)
                where KEY is an atom that begins with  k
                      ITEM is defined above
                      PERSON is a string
                      DUEDATE is an int
The borrowed predicate remembers who has borrowed items from the library. Here is a sample database:
===================================================

owns(k0, book('David Copperfield', 'Charles Dickens')).
owns(k1, book('Tale of Two Cities', 'Charles Dickens')).
owns(k2, book('Tale of Two Cities', 'Charles Dickens')).
owns(k3, dvd('Tale of Two Cities')).
owns(k4, book('Moby Dick', 'Herman Melville')).

borrowed(k2, 'Homer', 44).
borrowed(k4, 'Homer', 46).
borrowed(k3, 'Lisa', 92).
borrowed(k0, 'Lisa', 92).

===================================================
Whenever someone borrows an item, the appropriate borrowed premise is added to the database. When someone returns an item, the borrowed premise is removed. (Prolog has special operators, asserta and retract, for adding and removing premises from an active database.)

Given the above database, here are some queries. First, what has Homer borrowed?

?- borrowed(K, 'Homer', _).
K = k2 ;
K = k4 ;
false.
The database is written so that the items' keys are retrieved. We can use them: What books has Homer borrowed?
?- borrowed(K, 'Homer', _), owns(K, book(_,_)).
K = k2 ;
K = k4 ;
false.
The above example is important --- it shows how to retrieve a key of a book (K) and how to use the key as well as the data-structure name (functor) to match only the books borrowed by Homer. The action of matching functor names to the database is called unification, and it gives much power to Prolog. Note also that the _ symbol is a ``dummy variable'' that we do not care about --- its value is not printed. (Here we do not care about the book's due date nor the title and authors, so three dummy variables are used in the query.)

Of course, if we wished to see the titles, we modify the query like this:

?- borrowed(K, 'Homer', _), owns(K, book(T,_)).
K = k2,
T = 'Tale of Two Cities' ;
K = k4,
T = 'Moby Dick' ;
false.

Libraries want to track overdue books. Here is a law for proving when a borrowed item is overdue:

===================================================

isOverdue(Person, Item, Today) :- borrowed(Item, Person, DueDate),  Today > DueDate.

===================================================
The words. Person, Item, and Today are logical variables, because they begin with capital letters. Today is an int. (See our definitions earlier.) This law uses arithmetic on ints: it compares the int-value of Today with the int value of DueDate, to see if today is greater than the due date. If so, the goal, Today > DueDate succeeds (is proved).
?- isOverdue('Homer', _, 89).
true ;
true ;
false.

?- isOverdue('Homer', Item, 89).
Item = k2 ;
Item = k4 ;
false.
We query the database and learn that, if today is day 89, then Homer has two items overdue; we can learn the keys of the items if we wish.

Perhaps a fine is calculated by how many days an item is overdue. We can use simple arithmetic in Prolog, like this:

===================================================

hasFine(Person, Item, Today, HowMuch) :- borrowed(Item, Person, DueDate),
                                         isOverdue(_, Item, Today),  
                                         HowMuch is  Today - DueDate.

===================================================
The predicate, HowMuch is Today - DueDate is an assignment --- a matching --- of HowMuch to the answer computed from Today - DueDate. This ``subgoal'' succeeds when Today and DueDate are bound to numbers so that the subtraction can be computed.
?- hasFine('Homer', I, 89, Fine).
I = k2,
Fine = 45 ;
I = k4,
Fine = 43 ;
false.
You can also do simple arithmetic with +,*,-,/. You can compare values with >, <, =>, =<, =, \=.

Finally, it would be helpful to write a query that would return a list of all the items that Homer has borrowed. To do this, we must learn how to use lists in Prolog.


7.3.1 List data structures

Prolog's lists are like Python's lists; they are written like this:
[a, b, c]

[2,4,6,8]

['Homer', book('Tale of Two Cities', 'Charles Dickens')), book('Moby Dick', 'Herman Melville')) ] 

['Homer', [ book('Tale of Two Cities', 'Charles Dickens')),
            book('Moby Dick', 'Herman Melville')) ] ]

[]
As in Python, lists can be a mixture of values, and lists can be placed within lists. The list builder, [_,_, ..., _], is a functor, just like book(_,_) and dvd(_), seen in the previous section.

Unlike Python, Prolog does not use ints to index individual elements from a list. Instead, you can index the front (``head'') element of a list, and you can index the rest (``tail'') of the list without its front element. This head-tail indexing is done with a pattern matching that takes a little practice. The pattern looks like this: [H|T].

If we match the [H|T] pattern against [a,b,c], we have H=a, T=[b,c].

If we match the pattern against ['Homer', book('Tale of Two Cities', 'Charles Dickens')), book('Moby Dick', 'Herman Melville')) ] we have

H = 'Homer'
T = [  book('Tale of Two Cities', 'Charles Dickens')), book('Moby Dick', 'Herman Melville')) ]

If we match the pattern against ['Homer', [ book('Tale of Two Cities', 'Charles Dickens')), book('Moby Dick', 'Herman Melville')) ] ], we have

H = 'Homer'
T = [[ book('Tale of Two Cities', 'Charles Dickens')),
       book('Moby Dick', 'Herman Melville')) ]]

If we try to match [H|T] against [], it fails --- there is no match.

We use the [H|T] pattern to write ``functions'' that compute on lists. Here is a function that looks at a list (the first argument) and returns the head of the list as its answer (the second argunent):

getFirst([H|T], H) :- .
This law has no subgoals. When we use it in Prolog, we type just this (not like above):
getFirst([H|T], H).
We use the ``function'' like this:
?- getFirst([a,b,c], Ans).
Ans = a.
    
?- getFirst([], Ans).
false.
The first query asks if there is a value for Ans that ``proves'' getFirst([a,b,c], Ans). The law, getFirst([H|T], H), says we merely set the second argument equal to the head of the first argument and the Prolog interpreter proves the goal with this assignment to the logical variables:
Ans=H
H=a
T=[b, c]

Here is another example:

===================================================

getRest([H|T], T).

?- getRest([a,b,c], Ans).
Ans = [b,c].

?- getRest([], Ans).
false.

===================================================
Here is a third example, which extracts the second element in a list that has at least two elements:
===================================================

getSecond([A|[B|C]], B).

?- getSecond([a,b,c], Ans).
Ans = b.

===================================================
These small examples show that there is a style to writing predicates in Prolog so that they behave like functions. In C or Python, we write functions in this style:
def f(x,y) :
    . . .
    return ans
The parameters, x and y are listed separately from the ans. But in Prolog, we write,
f(x, y, ans) :- . . .
where the ans variable is written next to the parameters. A value is ``assigned'' to ans by Prolog's matching/unification operations that are used when the Prolog interpreter tries to solve a goal.

This style of writing a function as a predicate with parameters and answer variable grouped together is called relational programming.

We can make Prolog-style functions powerful with recursive references. In Python, we might search a list a for an element, v, like this, using recursive calls:

def member(v, a):
    if len(a) > 0 :              # is list  a  nonempty ?
        H = a[0]
        if H == v :              # is  v  at head of  a ?
           return True
        else :
           T = a[1:]
           return  member(v, T)  # if not, search inside tail of  a
    else : 
        return False             # list  a  is  []
We write this same recursive search in Prolog like this: (Important: the order of the clauses is crucial to the success of the proof search!)
===================================================

member(V, [V|_]).
member(V, [_|T]) :- member(V, T).

===================================================
We test the function with this example:
?- member(c, [a,b,c]).
Here's what happens:
  1. To prove the goal, the interpreter tries to match it against member(V, [V|_]). This fails, because atom c is not the same as atom a within list [a,b,c].

    The next law is tried, matching against member(V, [_|T]). This match succeeds. The new goal is member(V,T), where

    V=c
    T=[b,c]
    

  2. To prove goal member(V,T), that is, member(c,[b,c]), a match is attempted against member(V, [V|_]). This fails.

    Next, a match of member(c,[b,c]) again member(V, [_|T]) against member(c,[b,c]) is attempted. This succeeds. Since this premise was used once before, its logical variables, V and T, were used once before, so new variants, V1 and T1, are invented. The assignments to the logical variables look like this:

    V=c
    T=[b,c]
    V1=V
    T1=[c]
    
    and the new goal is member(V1,T1), that is, member(c,[c]). Remember: if a premise is used more than once to build a proof, new instances of its logical variables are created for each successful match. (Of course, this is because there is an implicit use of FORALL in the premise, and we are using the premise multiple times with the FORALLe rule!)
  3. Finally, the goal, member(c,[c]), matches against the first premise, and H=c. The proof is completed:

    V=c
    T=[b,c]
    V1=V
    T1=[c]
    H=V1
    
    ?- member(c, [a,b,c]).
    yes
    

If we ask the Prolog interpreter to trace the query,

?- trace(member).
we see this:
[debug] 3 ?- member(c, [a,b,c]).
 T Call: (7) member(c, [a, b, c])
 T Call: (8) member(c, [b, c])
 T Call: (9) member(c, [c])
 T Exit: (9) member(c, [c])
 T Exit: (8) member(c, [b, c])
 T Exit: (7) member(c, [a, b, c])
true 
By the way, member is a built-in, precoded predicate in Prolog. Try it on these examples and try to explain the answers:
===================================================

?- member(X, [1,2,3]).
X = 1 ;
X = 2 ;
X = 3 ;
false.

?- member(2, L).
L = [2|_G1043] ;
L = [_G1042, 2|_G1046] ;
L = [_G1042, _G1045, 2|_G1049] ;
L = [_G1042, _G1045, _G1048, 2|_G1052] ;
L = [_G1042, _G1045, _G1048, _G1051, 2|_G1055] ;
L = [_G1042, _G1045, _G1048, _G1051, _G1054, 2|_G1058] ;
   etc.

?- member(X, L).
L = [X|_G1058] ;
L = [_G1057, X|_G1061] ;
L = [_G1057, _G1060, X|_G1064] ;
L = [_G1057, _G1060, _G1063, X|_G1067] ;
L = [_G1057, _G1060, _G1063, _G1066, X|_G1070] ;
L = [_G1057, _G1060, _G1063, _G1066, _G1069, X|_G1073] ;
   etc.

===================================================

In a similar way, we can write a Prolog function to see if a value is not a member of a list: (Here, \= means !=.)

===================================================

notMember(V, []).
notMember(V, [H|T]) :- V \= H,  notmember(V, T).

===================================================
This two-line function computes the same steps as this Python function:
def notMember(v, a):
    if a == [] :
        return True
    else :
        if v != a[0] :
           return  notMember(v, a[1:])  #search tail of  a
        else :
           return False                 # v == a[0], so stop

Here is a third example, where we write a function that accepts a list of ints and constructs a list that has all the ints doubled in value. It will behave like this:

?- double([1,2,3], Y).
Y = [2,4,6].

?- double([], Y).
Y = [].
Here is the function:
===================================================

double([], []).
double([H|T], [HH|TT]) :-  HH is 2 * H,  double(T, TT).

===================================================
The computation made by double looks like this in Python:
def double(a) :
    if  a == [] :
        return []
    else :
        HH = a[0]
        TT = double(a[1:])
        return [HH] + TT
Here is a fourth example, which totals the ints in a list:
===================================================

total([], 0).
total([N|Rest], Answer) :- total(Rest, Subanswer),
                           Answer is N + Subanswer.

?- total([1,2,3,4], T).
T = 10.

===================================================


7.3.2 A control structure: findall

We should finish the library-database example from the previous section. How do we compute a list holding all the items borrowed by a user? We can do it with a predicate that uses lists, patterns, and recursion. But this issue appears so often in practice that Prolog has a built-in predicate, findall, that does the job for us. Here is how to define the list of books borrowed by 'Homer':

?- findall(Item, borrowed(Item, 'Homer', _), AnswerList).
AnswerList = [k2, k4].
The predicate, findall(WHAT, PREDICATE, ANSWERLIST), repeatedly proves PREDICATE, collecting each value of variable WHAT appearing in PREDICATE, saving the values in a new list named ANSWERLIST.

In the above example, borrowed(Item, 'Homer', _) is executed, and Item = k2. Then, borrowed(Item, 'Homer',_) is executed again, and Item = k3. Then, borrowed(Item, 'Homer',_) is executed again, and there is failure (false). The two answers are collected into the list, AnswerList = [k2, k4].

Read findall(X, PREDICATE, ANSWERLIST) like this:
``Find all the items, X, that make PREDICATE hold true and save them in a list named ANSWERLIST.''

We use our new knowledge to define this useful predicate:

===================================================

borrowedItems(Person, List) :- findall(Item, borrowed(Item, Person, _), List).

===================================================
so that borrowedItems('Homer', L) generates the list, L = [k2, k4], of items Homer borrowed. Note that borrowedItems('Marge', L) generates L = [] because Marge has nothing borrowed.

Similarly, we see that

?- findall(Item, borrowed(Item, _,_), AnswerList).
AnswerList = [k2, k4, K3, k0]
lists all items borrowed by all persons. and
?- findall(DueAt, borrowed(K, _, DueAt), DateList).
DateList = [44, 46, 92, 92].
lists the dates all borrowed items are due. Notice that the value(s) of K are not displayed. But you can use findall to save multiple values, say the Item and DueDate of each book borrowed:
?- findall([Item,DueDate], borrowed(Item, 'Homer', DueDate), Ans).
Ans = [[k2, 44], [k4, 46]].

If you understood this example, then you can do these exercises:

  1. Compute a list of all overdue items borrowed by a patron.
  2. Compute the total fine for all overdue items.

By the way, many functional and scripting languages, e.g., Python, have their own versions of findall that operate on list arguments --- it is called a list comprehension operator. Here is how you do it in Python:

ANSWERLIST = [ OP(X)  for X in ARGUMENTLIST  if PREDICATE ]
Here is some Python code:
nums = [0,1,2,3,4,5,6,7,8,9]   # you can also say:  nums = range(10)
# we collect the even-valued ints in   nums   and triple them:
answer = [ 3*n  for n in nums  if n % 2 == 0 ]  
print answer   #  prints  [0,6,12,18,24]
How do code this in C? In Java or C# ?


7.4 Example: Change Making

Prolog is useful for performing exhaustive search --- a skill valuable in data mining and artificial intelligence. Here is a small example that exploits this. It is from http://www.csupomona.edu/~jrfisher/www/prolog_tutorial:

This Prolog program checks and generates combinations of quarters, dimes, nickels, and pennies that total to one dollar:

===================================================

change([Q,D,N,P]) :- 
        member(Q,[0,1,2,3,4]),                  /* quarters     */ 
        member(D,[0,1,2,3,4,5,6,7,8,9,10]) ,    /* dimes        */ 
        member(N,[0,1,2,3,4,5,6,7,8,9,10,       /* nickels      */ 
                   11,12,13,14,15,16,17,18,19,20]),  
        S is 25*Q +10*D + 5*N, 
        S =< 100, 
        P is 100-S. 

===================================================
Examples:
?- change([Q,D,N,P]). 
lists all possible ways of giving change for a dollar. We can also check if a proposed quantity of change totals a dollar:
?- change([2,3,4,6]). 
no 
since 2 quarters, 3 dimes, 4 nickels, and 6 pennies do not make a dollar, and
?- change([2,3,2,P]). 
P=10 
calculates how many pennies are needed to make all the coins total a dollar.

Notice that less-than-equals is written =< in Prolog.


7.5 Example: Sorting

Here is a second, famous example. We say that an input list (or an array) is sorted into a new list if the new list is a permutation (reordering) of the input list and is ordered (its elements are arranged according to a comparison operator). Just stating this requirement becomes a solution to the problem in Prolog:
===================================================

/* sorted(Xs, Ys)  holds when  Ys  is the sorted variant of Xs */
sorted(Xs, Ys) :- permutation(Xs, Ys), ordered(Ys).

/* permutation(Xs, Zs)  holds true if Zs is a reordering of Xs */
permutation([], []).
permutation(Xs, [Z|Zs]) :- select(Z, Xs, Ys),  permutation(Ys, Zs).

/* select(X, HasAnX, HasOneLessX)  "extracts" X from  HasAnX, giving HasOneLessX */
select(X, [X|Rest], Rest).
select(X, [Y|Ys], [Y|Zs]) :- select(X, Ys, Zs).

/* ordered(Xs)  holds true if the elements in Xs are ordered by =< */
ordered([]).
ordered([X]).
ordered([X,Y|Rest]) :- X =< Y,  ordered([Y|Rest]).

===================================================
These clauses define what it means for list Ys to be a sorted version of input list Xs. Yet, when they are read by the Prolog interpreter, e.g.,
?- sorted([5,3,7,2,9,1], Ans)
the interpreter will expand the definitions of permutation and ordered, generating all possible permutations of the input list and checking to see which permutation is also ordered. The one that is ordered binds to Ans:
Ans = [1,2,3,5,7,9]
The interpreter "executed" the specification, and in doing so, located the answer!

This worked because the Prolog interpreter added its own control structure to the definitional clauses. In this sense, "specification + control = algorithm" is the slogan behind Prolog --- the human provides a specification of the problem, the interpreter provides control structure, and the result is an algorithm that computes the solution.

Alas, the algorithm synthesized here is slow and naive --- it grinds through all permutations of the input list to find the very one that is also ordered. A human can provide a bit of guidance to narrow the generation of the permutations. One example of such guidance is insertion sort, where a permutation is constructed by removing elements from the input list and moving them to the output list, inserting them in the appropriate position with respect to =<. Here is how insertion sort is coded in Prolog:

===================================================

/* isort(Xs, Ys)  generates  Ys  so that it is the sorted variant of Xs */
isort([], []).
isort([X|Xs], Ys) :-  isort(Xs, Zs), insert(X, Zs, Ys).

/* insert(X, L, LwithX)  inserts element X into list L, generating  LwithX */
insert(X, [], [X]).
insert(X, [Y|Ys], [X, Y | Ys]) :-  X =< Y.
insert(X, [Y|Ys], [Y|Zs]) :-  X > Y,  insert(X, Ys, Zs).

===================================================
Only one permutation of Xs is built --- the ordered one.

Prolog works great for game playing, where multiple searches must be made to calculate the consequences of all next possible moves. (Both sorting and change-making are "games" where the "player" must choose how to order elements or add coins.) Prolog is also great for solving problems that have no best strategies and must be solved by trial and error. If we add a few heuristics to narrow the search space of such trials, then the number of errors are reduced, and efficient solutions can appear.


7.6 A few additional useful Prolog predicates

The simplest way to use Prolog is to type the database clauses into a file, named, say, myfile.pl. Start the file by double-clicking on its icon --- you will receive a command window into which you can type queries. If you wish to update the clauses in myfile.pl, edit it, stop the command window, and restart it.

In addition to the commands shown in the previous examples, here are a few more.


7.7 An implementation you can use

You can download a free copy of SWI-Prolog at www.swi-prolog.org. Once you install it, the simplest way to use it is to use a text editor to type a file that holds your Prolog database. Name the file with the extension, .pl (e.g., myProgram.pl. When you double-click on the icon for your file, this starts the Prolog interpreter, which loads the contents of your file into its internal database. A command window next appears, into which you can type queries. When you must modify your program, close the command window, edit your program, and restart it.


7.8 Conclusion

We have only begun to explore the power that comes from modelling computation within predicate logic. With the addition of lists and recursively defined predicates, one has enough computational power such that any problem whose solution can be written in any programming language at all can also be written in Prolog.