There are many implementations of PROLOG, both free and commercial. A list of
freeware implementations can be found at:
http://www.cs.cmu.edu/Groups/AI/html/faqs/lang/prolog/prg/part2/faq-doc-2.html
On our departmental machines, we have SWI-Prolog installed. The binary can be run from:
/usr/local/gprolog
and the manual can be downloaded from there as well.
PROLOG is interactive, like LISP. You can type things into it directly. Once you are running PROLOG, you can quit by typing:
halt.
In the rare case that you get into a situation where Prolog does not give you back a new prompt (it could be in the debugger mode, or it could be waiting for more input, in which case SWI-Prolog displays a vertical bar), try pressing Ctrl-D.
Like LISP, programs in PROLOG are editted as independent files and loaded in. The content of PROLOG programs will be discussed below. Each time you make a change to a program, you must re-load. The command to load a file called file.pl is:
consult('file.pl').
It is customary to give PROLOG programs the .pl extension.
A PROLOG program consists of a sequence of Horn clauses. Horn clauses are like rules in first-order logic. They begin with a predicate called the 'head', which is the consequent of the rule. If the rule has any antecedents, then the head is followed by the symbol :- and then list of antecedents separated by commas, which is called the body. Horn clauses are always terminated with a period. Here is an example that says that people who have a lot of money are rich:
rich(X) :- person(X),haveLotsOf(X,money).
The antecedents are predicates, and the arguments can be terms, variables, or functions. Any non-function term that starts with a capital letter is interpreted as a variable; any non-function term that starts with a lower-case letter (or a digit) is interpreted as a constant. Predicate names must start with a lower-case letter, but may have upper-case letters, digits, or under-scores in them. PROLOG is case-sensitive, so be consistent.
Suppose you type in the following program as file.pl:
% this is file.pl
foo(a). % two facts
foo(b). bar(Thing) :- foo(Thing). % a rule
Then you load this program into PROLOG via consult('file.pl'). Now we can ask questions of the knowledge base. Queries are typed into the command line as a comma-separated list of predicates (called goals), which is interpreted as a conjunction. PROLOG tries to prove each goal in turn by either looking up facts directly, or back-chaining through rules. If it succeeds, it returns Yes. If it cannot find a proof, it returns No.
When there are variables in the query, there are often multiple solutions possible. If PROLOG can prove the query in this case, it returns a binding list (or unifier or substitution) for any variables in the query that became instantiated in the proof. Then PROLOG waits for an indication from the user of what to do next. If the user presses Enter, PROLOG returns to the interactive prompt. However, if the user presses ; then PROLOG will use back-tracking to attempt to find another solution via an alternative proof, and the whole process iterates.
Here is a transcript that shows PROLOG returning answers to various queries, given the above knowledge base...
15 ?- foo(a).
Yes
16 ?- foo(c).
No
17 ?- foo(V).
V = a ;
V = b ;
No
18 ?- bar(a).
Yes
19 ?- bar(V).
V = a ;
V = b ;
No
An interesting capability that PROLOG adds over real Horn clauses in first-order logic is the use of negation (true Horn clauses can't have negated antecedents). Antecedents in a rule may be preceeded with the symbol 'not', such as 'not P(X)'. When such a goal in encountered during the process of proving a query, an attempt is made to prove the predicate P(X) by itself first. If the proof succeeds, the the goal of 'not P(X)' will fail and PROLOG will be forced to back-track. If P(X) fails, then the goal of 'not P(X)' will succeed, and the original proof will move on to trying to prove the next goal. This procedural description of how negation works differs from the semantics of true negation in first-order logic. This "negation-as-failure" approach leads to certain conclusions that would not be entailed by first-order model theory. In particular, it leads to an assumption that anything that is not explicitly stated or provable is false, which is called the closed world assumption (CWA). It is important to keep in mind the different meaning of 'not', but usually it can be used in PROLOG effectively because it's interpretation is often close to the intended usage in many domains. Here is an example that uses negation to state that all birds except for opus can fly:
fly(X) :- bird(X),not abnormalWithRespectToFlying(X).
bird(tweety).
bird(opus).
abnormalWithRespectToFlying(opus).
22 ?- fly(tweety).
Yes
23 ?- fly(opus).
No
PROLOG has some special syntax for manipulating lists. Lists can be used as terms in predicates or functions. A list is bounded by square brackets and contains a comma-separated list of member terms, for example [a,b,c]. The empty list is simply represented by []. PROLOG has some built-in predicates pertaining to lists. For example, member(X,Y) is true whenever X is a member of the list Y, and intersection(X,Y,Z) can be used to compute the intersection of X and Y (if you specify) them, which will be returned as the binding to Z (if you give it as a variable in a query).
25 ?- member(b,[a,b,c]).
Yes
27 ?- intersection([a,b,c,d],[d,e,a,f],U).
U = [a, d]
PROLOG is also able to de-construct lists into heads and tails (like car and cdr in LISP, or first and rest). Within a list, a vertical line is used to separate the car from the cdr. This is most often used with two variables, which, when unified with a list, will be bound to the first element and the rest of the list, respectively. For example:
shopping_list([milk,eggs,bread,butter]). % a fact added to file.pl
29 ?- shopping_list([X|Y]).
X = milk
Y = [eggs, bread, butter]
Using this notation, you can actually redefine the member function in the following way:
% anything is a member of a list in which it is the first element
my_member(X,[X|Y]).
% otherwise, check to see if it is a member of the rest of the list
my_member(X,[Y|Z]) :- my_member(X,Z).
PROLOG can be made to do some simple arithmetic. Use 'is' to bind a variable to the result of some evaluated expression. For example,3
next(X,Y) :- Y is X+1.
30 ?- next(5,C).
C=6
However, PROLOG's ability to manipulate arithmetic is pretty weak. For example, it won't allow you to solve such a goal when X is unbound. For example, we would like next(D,9) to return D=8, but PROLOG just invokes an error. More powerful PROLOG-like systems called constraint-logic programming (CLP) languages are capable of solving more general problems like this. But PROLOG's limited arithmetic capabilities are still useful in some situations. You can also do subtraction, multiplication, and division.
PROLOG can also reason about equality, to a certain extent. In this case, at least one of the arguments must be instantiated. If both are, then the equality is either True or False. If one of the arguments is an unbound variable, then it is bound to the other variable and added to the substitution list. Inequalities between numbers, such as 'X<5' are also legal. Equality actually works for symbolic terms as well as numbers.
42 ?- next(2,H),G=H,next(G,I).
H = 3
G = 3
I = 4
Strings in PROLOG are enclosed in single quotes. To print something to the screen, use the write() predicate with whatever argument you want. It can be a string or a number. The predicate nl (with no arguments) prints a new line. Various PROLOG's may have more sophisticated output routines, similar to LISP's format function. By the way, PROLOG programs can also be compiled to run more efficiently.
Monday - Friday:
7 am - Midnight
Saturday:
10 am - 7 pm
Sunday:
12 pm - Midnight
Hours subject to change during holidays, emergencies, and summer semester.