The Department of Computer Science & Engineering
cse@buffalo

CSE 305
Programming Languages
Lecture Notes
Stuart C. Shapiro
Fall, 2003


Logic Programming Languages: Prolog

Prolog, which stands for Programming in Logic, is the major logic programming language in current use. Although there are several variants, we will consider Prolog as implemented by the most usual compiler implementations.

Logic programming is also called relational programming, as opposed to functional programming.

Mathematical facts:

Example1: Consider the Common Lisp function append that takes two lists and returns a list containing all the members of the first list followed by all the members of the second list. append is predefined in Common Lisp, but could be written as:
test(10): (defun append (list1 list2)
	      (if (endp list1)
		  list2
		(cons (first list1) (append (rest list1) list2))))
append

test(11): (append '(a b c) '(d e f))
(a b c d e f)
Notice that append is a function of the form f(x1, x2) = y, where if x1 = (a b c) and x2 = (d e f), then y = (a b c d e f).

The prolog version of append is a three-argument relation, append(X, Y, Z) which is true if Z is a list whose members are the members of X followed by the members of Y.

Note:

Here is the Prolog version of append:
append([], List2, List2).
append([First | Rest], List2, [First | Intermediate]) :-
	append(Rest, List2, Intermediate).
And here is an interactive test of that program:
<cirrus:Programs:1:103> sicstus
SICStus 3.9.1 (sparc-solaris-5.7): Thu Jun 27 22:58:18 MET DST 2002
Licensed to cse.buffalo.edu

| ?- consult('append.prolog').
% consulting /u0/faculty/shapiro/CSE305/Programs/append.prolog...
% consulted /u0/faculty/shapiro/CSE305/Programs/append.prolog in module user, 0 msec 392 bytes
yes

| ?- append([a, b, c], [d, e, f], X).
X = [a,b,c,d,e,f] ? 
yes

| ?- halt.
Interestingly, Prolog does not distinguish in parameters from out parameters. (They are all inout parameters.) It depends on which of the actual parameters are constants and which are variables. Here is another test of the append program:
<cirrus:Programs:1:104> sicstus
SICStus 3.9.1 (sparc-solaris-5.7): Thu Jun 27 22:58:18 MET DST 2002
Licensed to cse.buffalo.edu

| ?- consult('append.prolog').
% consulting /u0/faculty/shapiro/CSE305/Programs/append.prolog...
% consulted /u0/faculty/shapiro/CSE305/Programs/append.prolog in module user, 0 msec 392 bytes
yes

| ?- append([a, b, c], [d, e, f], X).
X = [a,b,c,d,e,f] ? 
yes

| ?- append(X, [d, e, f], [a, b, c, d, e, f]).
X = [a,b,c] ? 
yes

| ?- append([a, b, c], X, [a, b, c, d, e, f]).
X = [d,e,f] ? 
yes

| ?- halt.
The top-level syntax of a Prolog program is
<program> -> {<clause>}

<clause> -> <fact> | <rule> | ?- <query>
<fact> -> <head>.
<rule> -> <head> :- <body> .
<query> -> <body>.

<head> -> <relation>
<body> -> [<relation> {, <relation>}]

<relation> -> <predicate> [( <term> {, <term})]

You interact with Prolog by loading a program (by "consulting" a file), and then typing a query to the interactive system.

A <fact> is a relation that holds.

A <rule> says that the <head> holds if every <relation> in its <body> holds.

The set of <clause>s having the same <predicate> in their <head> constitutes a procedure for that <predicate>.

The scope of a variable is a single <clause> or <query>.

append was designed to compute the value of one actual argument when the other two were supplied. member is a two-argument relation designed to say whether its first argument is a member of the list which is its second argument. It is comparable to the Lisp member function:

test(15): (defun member (obj list)
	    (cond ((endp list) nil)
		  ((eql obj (first list)) t)
		  (t (member obj (rest list)))))
member

test(16): (member 'b '(a b c))
t

test(17): (member 'z '(a b c))
nil
Here is a first version of member in Prolog:
member(Obj, [Obj | Rest]).
member(Obj, [First | Rest]) :- member(Obj, Rest).

------------------------------------------
<cirrus:Programs:1:106> sicstus
SICStus 3.9.1 (sparc-solaris-5.7): Thu Jun 27 22:58:18 MET DST 2002
Licensed to cse.buffalo.edu
| ?- consult('member.prolog').
% consulting /u0/faculty/shapiro/CSE305/Programs/member.prolog...
* [Rest] - singleton variables in user:member/2
* Approximate lines: 1-2, file: '/u0/faculty/shapiro/CSE305/Programs/member.prolog'
* [First] - singleton variables in user:member/2
* Approximate lines: 2-3, file: '/u0/faculty/shapiro/CSE305/Programs/member.prolog'
% consulted /u0/faculty/shapiro/CSE305/Programs/member.prolog in module user, 0 msec 352 bytes
yes

| ?- halt.
The complaint is to a variable that appears in the head, but not in the body of a rule. Such a variable may appear as an "anonymous variable", written as _. No "anonymous variable" is the same as any other variable.

Here's the final version of member with some tests:

member(Obj, [Obj | _]).
member(Obj, [_ | Rest]) :- member(Obj, Rest).

-----------------------------------------------
<cirrus:Programs:1:107> sicstus
SICStus 3.9.1 (sparc-solaris-5.7): Thu Jun 27 22:58:18 MET DST 2002
Licensed to cse.buffalo.edu

| ?- consult('member.prolog').
% consulting /u0/faculty/shapiro/CSE305/Programs/member.prolog...
% consulted /u0/faculty/shapiro/CSE305/Programs/member.prolog in module user, 0 msec 328 bytes
yes

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

| ?- member(z, [a, b, c]).
no

| ?- halt.

The clauses in a procedure are tried in order, from the first to the last in the program, until one succeeds, thus constituting a multi-branch selection.

If the query contains a variable, then there may be multiple ways the program may succeed. The first way is shown, and the user may terminate the query by entering a CR, or may have the program find another way to succeed by entering ;. If the user keeps doing this, the program will ultimately fail (when there are no more ways to succeed):

<cirrus:Programs:1:108> sicstus
SICStus 3.9.1 (sparc-solaris-5.7): Thu Jun 27 22:58:18 MET DST 2002
Licensed to cse.buffalo.edu

| ?- consult('member.prolog').
% consulting /u0/faculty/shapiro/CSE305/Programs/member.prolog...
% consulted /u0/faculty/shapiro/CSE305/Programs/member.prolog in module user, 0 msec 328 bytes
yes

| ?- member(X, [a, b, c]).
X = a ? ;
X = b ? ;
X = c ? ;
no

| ?- halt.
Parameter Passing
Prolog's parameter passing is by matching. Example:
append([], List2, List2).
append([First | Rest], List2, [First | Intermediate]) :-
	append(Rest, List2, Intermediate).

------------------------------------------------------------
<cirrus:Programs:1:127> sicstus
SICStus 3.9.1 (sparc-solaris-5.7): Thu Jun 27 22:58:18 MET DST 2002
Licensed to cse.buffalo.edu

| ?- consult('append.prolog').
% consulting /u0/faculty/shapiro/CSE305/Programs/append.prolog...
% consulted /u0/faculty/shapiro/CSE305/Programs/append.prolog in module user, 0 msec 392 bytes
yes

| ?- append([X, b, [c], d], [e, X , Y], [a, b, [Y] | Z]).
X = a,
Y = c,
Z = [d,e,a,c] ? 
yes
This figure shows the matching variables, and how information flows to instantiate them:

Arithmetic
Arithmetic operators evaluate their arguments, which must be instantiated (fully bound). The values of expressions may be bound to a variable with is. Note the difference between = and is.
| ?- X = 3.
X = 3 ? 
yes

| ?- 3 = X.
X = 3 ? 
yes

| ?- X is 3.
X = 3 ? 
yes

| ?- 3 is X.
! Instantiation error in argument 2 of is/2
! goal:  _73 is _74

| ?- X = 3 + 5.
X = 3+5 ? 
yes

| ?- 3 + 5 = X.
X = 3+5 ? 
yes

| ?- X is 3 + 5.
X = 8 ? 
yes

| ?- 3 + 5 is X.
! Instantiation error in argument 2 of is/2
! goal:  _73 is _74

Sequence, Selection, and Loop
Statement-level control structures were illustrated by the echo.prolog program.

This version differs from the original in that it ends with a query that begins execution of the program. (Compare the automatic start of main in the C-based languages.

echo :- format("~nType something to each prompt.~n\c
                  Do not start with a capital letter.~n\c
	          End every input with a period.~n\c
	          Type bye to quit.~2n", []),
	prompt_and_read(1).

prompt_and_read(N) :- 
	format("~d: ", [N]),
	read(X),
	echo_and_loop(N, X).

echo_and_loop(_, bye):- format("Good bye.~n", []), halt.
echo_and_loop(N, X) :- 
	format("I read -->~a<--~n", [X]),
	N1 is N + 1,
	prompt_and_read(N1).

?- echo.

-----------------------------------------------------------
<cirrus:Programs:1:129> sicstus -l echo.prolog
% compiling /u0/faculty/shapiro/CSE305/Programs/echo.prolog...

Type something to each prompt.
Do not start with a capital letter.
End every input with a period.
Type bye to quit.

1: 'Hello there'.
I read -->Hello there<--
2: hi.
I read -->hi<--
3: bye.
Good bye.

% compiled /u0/faculty/shapiro/CSE305/Programs/echo.prolog in module user, 40 msec 6056 bytes
For another example, here's the factorial program in Prolog:
factorial(0, 1).
factorial(N, F) :- N > 0, Prev is N-1, factorial(Prev, M), F is N * M.
And here's factorial tutorial on the web, complete with an animated evaluation.

First Previous

Copyright © 2003 by Stuart C. Shapiro. All rights reserved.

Stuart C. Shapiro <shapiro@cse.buffalo.edu>