head :- body.where head is a Prolog structure and body is optionally a series of Prolog structures separated by commas. The ":-" symbol is called the neck and is often read as "if".
A number of clauses with the same functor and arity are said to define a predicate of that functor and arity, often written in the form "functor/arity". The clauses must be contiguous unless the predicate is mentioned in a 'multifile' or 'discontiguous' directive.
Here, for example, are three clauses defining a predicate called is_ordered/1.
is_ordered([]). is_ordered([E1]). is_ordered([E1, E2 | Rest]) :- is_less_than(E1, E2), is_ordered([E2 | Rest]).
Prolog has to have a method for matching the goal it is currently trying to prove against heads of clauses. When the head of a clause and the current goal match, the clause is chosen and Prolog goes on to try and prove the goals in its body (if any).
The act of matching two Prolog terms is called unification and is described by the following rules:
Prolog's backtracking top-to-bottom, left-to-right search is simple and effective. Backtracking works as follows:
Note that call/1, findall/3, bagof/3 and setof/3 access the database,
and for this reason, if they are compiled into modules with import and
export, the goals they call must be accessible, which means if they are
not in the local code they must be listed in an import directive.
This predicate can also be used in the form Goal1 -> Goal2, which only succeeds once, if at all, and if so executes Goal2.
Note that not(not(Goal)) has the interesting, and sometimes useful, effect of succeeding if Goal can be proved, but not unifying any of its variables and failing on backtracking.
For example, findall(X, a(X), [1, 2, 3]) is true if the database contains precisely the following clauses for a:
a(1). a(2). a(3).If Goal cannot be proved for any values of Instance, then List is unified with the empty list [].
findall is generally used to generate lists from database entries, so for example it might be used as follows (with the database shown above):
?- findall(X, a(X), L). L = [1, 2, 3]For a database containing:
fact(bird, duck). fact(sound, quack). fact(color, white).Then you can construct arbitrary terms with:
?- findall(Name : Value, fact(Name, Value), List). List = [bird : duck, sound : quack, color : white]
It is possible to convert an otherwise free variable to a non-free variable by using the ^ symbol as follows:
bagof(W, a(W, X, Y, Z), L). % Here X, Y and Z are free bagof(W, X ^ Y ^ a(W, X, Y, Z), L) % Here only Z is freeSo, for example, consider the following database:
likes(fred, beer). likes(tom, wine). likes(jane, beer). likes(jane, coke). ?- bagof(P, likes(X, P), L). % X is free so we will backtrack P = H47 X = fred L = [beer] ; P = H47 X = tom L = [wine] ; P = H47 X = jane L = [beer, coke] ; no ?- bagof(P, X ^ likes(X, P), L). P = H47 X = H48 L = [beer, wine, beer, coke].
?- get_preds(X). X = [ main / 0, goto / 1, ...
Note that like all such predicates, more than one goal can be given as an argument by enclosing them in parenthesis:
main :- first_backtracking_goal, once( ( first_once_goal, second_once_goal, third_once_goal )), next_backtracking_goal, ...
! is always true, so if a clause containing a cut is read as a statement of truth, it behaves as if there were no cut there. But cut affects the way backtracking is performed as follows:
Once a cut is executed, the choice of the clause which contains it is frozen as a proof step. Also any choices made during the proof of the goals between the head of the clause and the cut are frozen. Thus cut acts like a fence. When backtracking passes over the cut (heading left in a clause), then proof reconsideration continues not with the goal to the left of the !, but the goal to the left of the goal which chose the clause containing the cut.
repeat. repeat :- repeat.
On backtracking Increment is added to the current value of Index and Index is bound to this new value. Again a range check is performed.
?- for(X, 1, 5, 1), write(X), fail. 12345 no
The two key predicates' arguments are:
catch(Goal, Catcher, Recover)
Catch statements are subject
to the limitations of local predicates. If a catch is used in a module
(a file containing :-import or :-export statements) then the goals mentioned
in the catch must be global, not local.
The catch/throw pair allow the flow-of-control to skip over all of the intermediate predicates, which is especially useful for dealing with error exceptions.
Throw will only succeed if the term thrown matches an active catch term. After the goal of the catch is satisfied the catch term can no longer be thrown to. An attempt to throw an uncaught term results in a system error.
The following is the example from samples/prolog/misc/catch.pro:
main :- /* Catch and process all throws not handled by subsequent catches, including throw(quit) used to end the program. */ catch(doit, X, error_handler(X)). error_handler(quit) :- write($Quitting\n$). error_handler(X) :- write($Unknown Error$ : X), nl. doit :- repeat, write($Enter one or done or something else: $), read_string(S), string_atom(S, A), catch(do(A), badcommand(X), (write($Bad Command$:X),nl)), fail. do(one) :- catch(do_one, except(X), except(X)), !. do(done) :- throw(quit). do(X) :- throw(badcommand(X)). except(notinteger:X) :- write(X), write($ not an integer\n$). except(toosmall:X) :- write(X), write($ is too small\n$). except(toobig:X) :- write(X), write($ is too big\n$). do_one :- repeat, write($Enter a number between 10 and 20,\n$), write($'q' to quit, or anything else to see an error:\n$), read_string(S), string_term(S,T), check(T), fail. check(q) :- throw(quit). check(X) :- not(integer(X)), throw(except(notinteger:X)). check(X) :- X > 10, X < 20, !, write($Very good\n$). check(X) :- X =< 10, throw(except(toosmall:X)). check(X) :- X >= 20, throw(except(toobig:X)). check(X) :- throw(badcheck(X)). |
Note that multiple goals can be provided to catch/3, such as:
main :- catch( (goal1, goal2, goal3), error(X,Y), process_error(X,Y)).
Cut is very useful to control backtracking behavior in Prolog programs but its effects are confined only to the code of the predicate in which the clause containing the cut is a part. There are occasions in which we would like to modify backtracking behavior beyond the current predicate- an example of which is in handling errors.
To facilitate a form of more general backtracking control Amzi! Prolog has introduced the concept of the tag.
A tag is a marked instance of a choice point in the execution environment of a Prolog program. The choice point is marked by associating it with some term using the predicate tag/1. The predicate cut_tag/1 allows the removal of all choice points back to and including the choice point marked by a given term.
tag associates the most current choice point with Term. This association is only removed when the tagged choice point is removed by backtracking.
cut_tag scans back through the choice points until it comes across the first choice point with a tag which can be unified with Term. All choice points more recent than the tagged choice point are removed.
As an example, consider the use of tags to "fail out of" the infinite loop of a repeat (repeat is a special built in predicate which can always be reproved an unlimited number of times) so
repeat, do, failacts like an infinite loop.
loop :- tag(loop_marker), repeat, doit, fail. doit :- want_to_stop, cut_tag(loop_marker), !. doit :- do_something.When the clause for loop is entered it will tag the most recent choice point with label "loop_marker."
Then the repeat will be proved and doit will be tried. As long as want_to_stop is not provable, do_something will be proved and then fail will be tried. fail always fails, so backtracking takes us eventually back to repeat and doit is proved again.
Once we can prove want_to_stop, cut_tag(loop_marker) is proved. This looks back along the proof and finds that the choice point before repeat was marked with loop marker. So every choice point since then (including the choice point for repeat) is removed. Finally fail is proved one more time. But now the repeat has been effectively removed and so backtracking will go all the way back to some point before loop was entered.
Copyright ©1987-2000 Amzi! inc. All Rights Reserved.