The Prolog program is composed of predicates (procedures, record types, relations).
Each is defined by its name and a number called arity. The arity is the fixed number of arguments
(attributes, fields) the predicate has. Two predicates with the same name and different arity are
considered to be different predicates.
code example: ######################code example: ######################
% This is the syntax for comments.
% MORTAL - The first illustrative Prolog program
mortal(X) :- person(X).
person(socrates).
person(plato).
person(aristotle).
mortal_report:-
write('Known mortals are:'),nl,
mortal(X),
write(X),nl,
fail.
code example: ######################code example: ######################
In our sample program we saw three examples of predicates. They are: person/1,
mortal_report/0, and mortal/1.
Each of these three predicates has a distinctly different flavor.
person/1 :looks like multiple data records with one data field for each.
mortal_report/0:looks like a procedure with no arguments.
mortal/1:a logical assertion or rule that is somewhere in between data and procedure.
In the example program, the predicate person/1 has three clauses. The other predicates have only
one clause.
A clause can be either a fact or a rule. The three clauses of the person/1 predicate are all facts.
The single clauses of mortal_report/0 and mortal/1 are both rules.
predicate:
clause:
backtracking:
unification:
The SWI-Prolog Tracer
use tis command to debug(trace) the prolog program:
?- trace, p(X,Y,Z).
after this command, your prolog code will be traced,
when you want to end the trace, just use notrace. then the trace function is turned off.
Compound Queries
Simple goals can be combined to form compound queries. For example, we might want to know if there is anything good to eat in the kitchen. In Prolog we might ask
?- location(X, kitchen), edible(X).
Whereas a simple query had a single goal, the compound query has a conjunction of goals. The comma separating the goals is read as "and."
Logically (declaratively) the example means "Is there an X such that X is located in the kitchen and X is edible?" If the same variable name appears more than once in a query, it must have the same value in all places it appears. The query in the above example will only succeed if there is a single value of X that can satisfy both goals.
This logical query can also be interpreted procedurally, using an understanding of Prolog's execution strategy. The procedural interpretation is: "First find an X located in the kitchen, and then test to see if it is edible. If it is not, go back and find another X in the kitchen and test it. Repeat until successful, or until there are no more Xs in the kitchen."
Rules
We said earlier a predicate is defined by clauses, which may be facts or rules. A rule is no more than a stored query. Its syntax is
head :- body.
where
- head
- a predicate definition (just like a fact)
- :-
- the neck symbol, sometimes read as "if"
- body
- one or more goals (a query)
For example, the compound query that finds out where the good things to eat are can be stored as a rule with the predicate name where_food/2.
where_food(X,Y) :-
location(X,Y),
edible(X).
It states "There is something X to eat in room Y if X is located in Y, and X is edible."
ust as we had multiple facts defining a predicate, we can have multiple rules for a predicate. For example, we might want to have the broccoli included in where_food/2. (Prolog doesn't have an opinion on whether or not broccoli is legitimate food. It just matches patterns.) To do this we add another where_food/2 clause for things that 'taste_yucky.'
where_food(X,Y) :-
location(X,Y),
edible(X).
where_food(X,Y) :-
location(X,Y),
tastes_yucky(X).
Now the broccoli shows up when we use the semicolon (;) to ask for everything.
?- where_food(X, kitchen).
X = apple ;
X = crackers ;
X = broccoli ;
no
Until this point, when we have seen Prolog try to satisfy goals by searching the clauses of a predicate, all of the clauses have been facts.
Using Rules
Using rules, we can solve the problem of the one-way doors. We can define a new two-way predicate with two clauses, called connect/2.
connect(X,Y) :- door(X,Y).
connect(X,Y) :- door(Y,X).
It says "Room X is connected to a room Y if there is a door from X to Y, or if there is a door from Y to X." Note the implied 'or' between clauses. Now connect/2 behaves the way we would like.
list_connections(Place) :-
connect(Place, X),
tab(2),
write(X),
nl,
fail.
list_connections(_).
We now have an understanding of the fundamentals of Prolog, and it is worth summarizing what we have learned so far. We have seen the following about rules in Prolog.
- A Prolog program is a logicbase of interrelated facts and rules.
- The rules communicate with each other through unification, Prolog's built-in pattern matcher.
- The rules communicate with the user through built-in predicates such as write/1.
- The rules can be queried (called) individually from the listener.
We have seen the following about Prolog's control flow.
- The execution behavior of the rules is controlled by Prolog's built-in backtracking search mechanism.
- We can force backtracking with the built-in predicate fail.
- We can force success of a predicate by adding a final clause with dummy variables as arguments and no body.
We now understand the following aspects of Prolog programming.
- Facts in the logicbase (locations, doors, etc.) replace conventional data definition.
- The backtracking search (list_things/1) replaces the coding of many looping constructs.
- Passing of control through pattern matching (connect/2) replaces conditional test and branch structures.
- The rules can be tested individually, encouraging modular program development.
- Rules that call rules encourage the programming practices of procedure abstraction and data abstraction. (For example, look/0 doesn't know how list_things/1 works, or how the location data is stored.)
They can be used in rules as well. Here are two example predicates. One converts centigrade temperatures to Fahrenheit, the other checks if a temperature is below freezing.
c_to_f(C,F) :-
F is C * 9 / 5 + 32.
freezing(F) :-
F =< 32.
Here are some examples of their use.
?- c_to_f(100,X).
X = 212
yes
?- freezing(15).
yes
?- freezing(45).
no