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 compiler expects clauses to be contiguous unless the predicate is mentioned in a 'multifile' or 'discontiguous' directive, or is dynamic, indexed or sorted.
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:
When a clause is under consideration to match against a goal, space is reserved to hold variable bindings. If the clause is chosen again later on in the proof, then new space is reserved for the variable bindings caused by the new choice.
It is possible to construct a unification query with terms that refer to impossible nested combinations. These are called cyclic terms. They cause the unification algorithm to go in an infinite loop, thus crashing the system. They can be caught by turning on occurs_check using either Prolog flags or the amzi.cfg file.
It requires the unification of multiple cyclic terms to cause the problem. For example:
?- A = f(A), B = f(B), A = B.
The first two unifications are cyclic, and when the third unification between the cyclic terms is tried, that's when the infinite loop happens. With occurs_check on, the above query will fail at the final A = B.
Clearly occurs_check seems like a good idea, but because these situations are rare, and because occurs_check adds about 10% performance overhead, the ISO standard default for it is off.
Note that only complex situations, like the one above cause the problem. Single cyclic unification doesn't cause a unification problem until it is output, and the output routines automatically detect that situation, whether occurs_check is on or off. A '...' is displayed to indicate the cyclic term after a few iterations.
?- A = f(A). A = f(f(f(f(...))))
If the Prolog flag occurs_check is turned on, the default is off, then attempts to unify cyclic terms will simply fail, rather than crash. This normally isn't necessary because it is usually an unusual error that causes the problem.
?- X = [X|Y], is_cyclic(X). X = [[[[... | H182] | H182] | H182] | H182] Y = H182 yes
We have seen that Prolog uses unification to match a goal to the head of a clause, but if there are several candidate clauses, which does Prolog choose to try first? In most cases Prolog looks through the clauses in the order in which they are entered in the logicbase. This is not the case for a 'sorted' predicate, which is, as the name implies, stored in sorted order.
Prolog's backtracking top-to-bottom, left-to-right search is simple and effective. Backtracking works as follows:
In addition to simple predicates, Amzi! Prolog may be given compound or complex goals. And for each of the predicates below that takes a goal as an argument, that goal can be a complex goal, often requiring parenthesis.
The following example shows the call/1 predicate first calling a simple goal, then calling the 'and' operator ','/2 which joins three goals together.
call( text(X) ), call( (test1(X), test2(Y), test3(Z)) ), ...
call/1 succeeds if Goal can be proved. Goal must be instantiated to a term which could be a valid goal in a clause body. Then call succeeds if and only if that goal can be proved. Note that Goal may be provable using compiled code or dynamic clauses, the call predicate handles both with no need for declarations. call/1 is often used for calling generated goals, for example:
G =.. [PRED, ARG1, ARG2, ARG3], call(G).
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.
\+/1 X is a synonym for not X.
For example, findall(X, a(X), [1, 2, 3]) is true if the logicbase contains 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/3 is generally used to generate lists from logicbase entries, so for example it might be used as follows (with the logicbase shown above):
?- findall(X, a(X), L). L = [1, 2, 3]Consider these facts in the logicbase:
fact(bird, duck). fact(sound, quack). fact(color, white).The terms in the list take the form of the first argument, as in this example:
?- 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 logicbase:
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].
!/0 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
catch/3 catches any errors that unify with ERROR thrown during the execution of GOAL. If an error is caught, control passes to HANDLER. If no errors are caught, catch/3 is the same as call/1.
catch/3 catches errors thrown by the application, using throw/1, or errors thrown by the system. System errors are thrown as error/2 structures:
error(ERROR_CLASS, ATTRIBUTE_VALUE_LIST)
The ATTRIBUTE_VALUE_LIST will vary depending on the type of error. Here's an example of a read error:
?- catch(read(X), E, write(E)). duck+*(le. error(syntax_error, [type = read, rc = 407, message = Unexpected operator, predicate = read_term$/3, callstack = - read_term3;- catch/3;... read_buffer = duck+*( **NEAR HERE** le., read_file = user_function, line_number = 0]) X = H1 E = % same as above
See below for details on application thrown errors and multiple catch/3 goals. See the Logic Server error handling documentation for more details on system errors.
throw/1 throws and error, and the error is TERM. TERM can be any valid Prolog term. If TERM unifies with the ERROR argument of a catch/3 above the throw/1 in the call stack, then that catch/3 HANDLER will handle the error.
If there is no catch/3 to handle the error, then the error is thrown to the calling application, and, if it doesn't handle it, eventually to the operating system. The calling application might include Logic Server exception handling, which can also catch errors thrown from Prolog code, or the runtime.
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)).
Copyright ©1987-2011 Amzi! inc. All Rights Reserved. Amzi! is a registered trademark and Logic Server is a trademark of Amzi! inc.