Logic programming is characterized by programming with relations and inference.
Keywords and phrases: Horn clause, Logic programming, inference, modus ponens, modus tollens, logic variable, unification, unifier, most general unifier, occurs-check, backtracking, closed world assumption, meta programming, pattern matching. set, relation, tuple, atom, constant, variable, predicate, functor, arity, term, compound term, ground, nonground, substitution, instance, instantiation, existential quantification, universal quantification, unification, modus ponens, proof tree, goal, resolvent.
A logic program consists of a set of axioms and a goal statement. The rules of inference are applied to determine whether the axioms are sufficient to ensure the truth of the goal statement. The execution of a logic program corresponds to the construction of a proof of the goal statement from the axioms.
In the logic programming model the programmer is responsible for specifying the basic logical relationships and does not specify the manner in which the inference rules are applied. Thus
Logic programming is based on tuples. Predicates are abstractions and generalization of the data type of tuples. Recall, a tuple is an element of
The squaring function for natural numbers may be written as a set of tuples as follows:
Such a set of tuples is called a relation and in this case the tuples define the squaring relation.
Abstracting to the name sqr and generalizing an individual tuple we can define the squaring relation as:
Parameterizing the name gives:
In the logic programming language Prolog this would be written as:
Note that the set of tuples is named sqr and that the parameters are X and Y. Prolog does not evaluate its arguments unless required, so the expression Y is X*X forces the evaluation of X*X and unifies the answer with Y. The Prolog code
may be read in a number of ways; it could be read P where Q or P if Q. In this latter form it is a variant of the first-order predicate calculus known as Horn clause logic. A complete reading of the sqr predicate the point of view of logic is: for every X and Y, Y is the sqr of X if Y is X*X. From the point of view of logic, we say that the variables are universally quantified. Horn clause logic has a particularly simple inference rule which permits its use as a model of computation. This computational paradigm is called Logic programming and deals with relations rather than functions or assignments. It uses facts and rules to represent information and deduction to answer queries. Prolog is the most widely available programming language to implement this computational paradigm.
Relations may be composed. For example, suppose we have the predicates, male(X), siblingof(X,Y), and parentof(Y,Z) which define the obvious relations, then we can define the predicate uncleof(X,Z) which implements the obvious relation as follows:
The logical reading of this rule is as follows: ``for every X,Y and Z, X is the uncle of Z, if X is a male who has a sibling Y which is the parent of Z.'' Alternately, ``X is the uncle of Z, if X is a male and X is a sibing of Y and Y is a parent of Z.'' %fatherof(X,Y),fatherof(Y,Z) defines paternalgrandfather(X,Z)uncleof(X,Z) <-- male(X), siblingof(X,Y), parentof(Y,Z).
The difference between logic programming and functional programming may be illustrated as follows. The logic program
is an abreviation forf(X,Y) <-- Y = X*3+4
which asserts a condition that must hold between the corresponding domain and range elements of the function. In contrast, a functional definition introduces a functional object to which functional operations such as functional composition may be applied.
Logic programming has many application areas:
Core PrologCLAUSE, FACT, RULE, QUERY, FUNCTOR, ARITY, ORDER, UNIVERSAL QUANTIFICATION, EXISTENTIAL QUANTIFICATION, RELATIONSP in ProgramsProgram ::= Clause... Query | Query
C in Clauses
Q in Queries
A in Atoms
T in Terms
X in Variables
Clause ::= Predicate . | Predicate :- PredicateList .
PredicateList ::= Predicate | PredicateList , Predicate
Predicate ::= Atom | Atom( TermList )
TermList ::= Term | TermList , Term
Term ::= Numeral | Atom | Variable | Structure
Structure ::= Atom ( TermList )
Query ::= ?- PredicateList .
Numeral ::= an integer or real number
Atom ::= string of characters beginning with a lowercase letter or encluded in apostrophes.
Variable ::= string of characters beginning with an uppercase letter or underscoreTerminals = {Numeral, Atom, Variable, :-, ?-, comma, period, left and right parentheses }
While there is no standard syntax for Prolog, most implementations recognize the grammar in Figure M.N.
Figure M.N: Prolog grammar P in Programs
C in Clauses
Q in Query
H in Head
B in Body
A in Atoms
T in Terms
X in Variable
P ::= C... Q...
C ::= H [ :- B ] .
H ::= A [ ( T [,T]... ) ]
B ::= G [, G]...
G ::= A [ ( [ X | T ]... ) ]
T ::= X | A [ ( T... ) ]
Q ::= ?- B .
In logic, relations are named by predicate symbols chosen from a prescribed vocabulary. Knowledge about the relations is then expressed by sentences constructed from predicates, connectives, and formulas. An n-ary predicate is constructed from prefixing an n-tuple with an n-ary predicate symbol.
A logic program is a set of axioms, or rules, defining relationships between objects. A computation of a logic program is a deduction of consequences of the program. A program defines a set of consequences, which is its meaning. The art of logic programming is constructing concise and elegant programs that have the desired meaning.
The basic constructs of logic programming, terms and statements are inherited from logic. There are three basic statements: facts, rules and queries. There is a single data structure: the logical term.
This fact states that the relation father holds between bill and mary. Another name for a relationship is predicate.father(bill,mary). plus(2,3,5). ...
Note that the text terminates queries with a question mark rather than preceding.?- father(bill,mary). ?- father(bill,jim).
There is a close relation between the axiomatic semantics of imperative programs and logic programs. A logic program to sum the elements of a list could be written as follows.
A proof of its correctness is trivial since the logic program is but a statement of the mathematical properties of the sum.sum([Nth],Nth). sum([Ith|Rest],Ith + Sum_Rest) <-- sum(Rest,Sum_Rest).
A[N] = sum_{i=N}^N A[i]} sum([A[N]],A[N]). sum_{i=I}^N A[i] = A[I] + S if 0 < I, sum_{i=I+1}^N A[i] = S} sum([A[I],...,A[N]], A[I]+S) <-- sum([A[I+1],...,A[N]],S).
The rule of instantiation (P(X) deduce P(c)). The rule of deduction is modus ponens. From A :- B1, B2, ..., Bn. and B1', B2', ..., Bn' infer A'. Primes indicate instances of the corresponding term.
The meaning M(P) of a logical program P is the set of unit goals deducible from the program.
A program P is correct with respect to some intended meaning M iff the meaning of P M(P) is a subset of M (the program does not say things that were not intended).
A program P is complete with respect to some intended meaning M iff M is a subset of M(P) (the program says everything that was intended).
A program P is correct and complete with respect to some intended meaning M iff M = M(P).
We can conclude b by modus ponens given that b <-- a and a. Alternatively, if b is assume to be false then from b <-- a and modus tollens we infer ¬ a but since a is given we have a contradiction and b must hold. The following program illustrates unification.a. b <-- a. b?
Consider the query `ancestor_of(a,b)?'. To answer the question ``is a an ancestor of b'', we must select the second rule for the ancestor relation and unify a with Anc and b with Desc. Interm then unifies with c in the relation parent_of(b,c). The query, ancestor_of(b,c)? is answered by the first rule for the ancestor_of relation. The last query is asking the question, ``Are there two persons such that the first is an ancestor of the second.'' The variables in queries are said to be existentially quantified. In this case the X unifies with a and the Y unifies with b through the parent_of relation. Formally,parent_of(a,b). parent_of(b,c). ancestor_of(Anc,Desc) <-- parent_of(Anc,Desc). ancestor_of(Anc,Desc) <-- parent_of(Anc,Interm) \wedge ancestor_of(Interm,Desc). parent_of(a,b)? ancestor_of(a,b)? ancestor_of(a,c)? ancestor_of(X,Y)?
Definition M.N: A unifier of two terms is a substitution making the terms identical. If two terms have a unifier, we say they unify.
For example, two identical terms unify with the identity substitution. concat([1,2,3],[3,4],List) and concat([X|Xs],Ys,[X|Zs]) unify with the substitutions {X = 1, Xs = [2,3], Ys = [3,4], List = [1|Zs]}
There is just one rule of inference which is resolution. Resolution is much like proof by contradiction. An instance of a relation is ``computed'' by constructing a refutation. During the course of the proof, a tree is constructed with the statement to be proved at the root. When we construct proofs we will use the symbol ¬ to mark formulas which we either assume are false or infer are false and the symbol [] for contradiction. Resolution is based on the inference rule modus tollens and unification. This is the modus tollens inference rule.
Notice that as a result of the inference there are several choices. Each ¬ A_{i} is a formula marking a new branch in the proof tree. A contradiction occurs when both a formula and its negation appear on the same path through the proof tree. A path is said to be closed when it contains a contradiction otherwise a path is said to be open. A formula has a proof if and only if each path in the proof tree is closed. The following is a proof tree for the formula B under the hypothesises A0 and B <-- A0,A_{1}.From ¬ B and B <-- A0,...,An infer ¬ A0 or...or ¬ An
There are two paths through the proof tree, 1-4, 5, 6 and 1-4, 7, 8. The first path contains a contradiction while the second does not. The contradiction is marked with [].1 From ¬ B 2 and A0 3 and B <-- A0,A_{1} 4 infer ¬ A0 or} ¬ A_{1} 5 choose ¬ A0 6 contradiction [] 7 choose ¬ A_{1} 8 no further possibilities} open}
As an example of computing in this system of logic suppose we have defined the relations parent and ancestor as follows:
where identifiers beginning with lower case letters designate constants and identifiers beginning with an upper case letter designate variables. We can infer that ogden is an ancestor of mikko as follows.1. parent_of(ogden,anthony) 2. parent_of(anthony,mikko) 3. parent_of(anthony,andra) 4. ancestor_of(A,D) <-- parent_of(A,D) 5. ancestor_of(A,D) <-- parent_of(A,X) 6. ancestor_of(X,D)
Notice that all choices result in contradictions and so this proof tree is a proof of the proposition that ogden is an ancestor of mikko. In a proof, when unification occurs, the result is a substitution. In the first branch of the previous example, the term anthonoy is unified with the variable X and anthony is substituted for all occurences of the variable X.¬ ancestor(ogden,mikko) the assumption ¬ parent(ogden,X) or} ¬ ancestor(X,mikko) resolution} ¬ parent(ogden,X) first choice ¬ parent(ogden,anthony) unification with first entry [] produces a contradiction ¬ ancestor(anthony,mikko) second choice ¬ parent(anthony,mikko) resolution [] A contradiction of a fact.}
UNIVERSAL QUANTIFICATION, EXISTENTIAL QUANTIFICATION
The unification algorithm can be defined in Prolog. Figure~\ref{lp:unify}
contains a formal definition of unification in Prolog
unify(X,Y) <-- X == Y.
unify(X,Y) <-- var(X), var(Y), X=Y. unify(X,Y) <-- var(X), nonvar(Y), \+ occurs(X,Y), X=Y. unify(X,Y) <-- var(Y), nonvar(X), \+ occurs(Y,X), Y=X. unify(X,Y) <-- nonvar(X), nonvar(Y), functor(X,F,N), functor(Y,F,N), X =..[F|R], Y =..[F|T], unify_lists(R,T). unify_lists([ ],[ ]).
occurs(X,Y) <-- X==Y.
occurs_list(X,[Y|R]) <-- occurs(X,Y).
|
To illustrate the inference rules, consider the following program consisting of a rule, two facts and a query:\begin{array}{l} \frac{A1 <-- B.}, ?- A1, A2,...,An.}}{?- B, A2,...,An.}} \frac{?- true, A1, A2,...,An.}}{?- A1, A2,...,An.}} \caption{Inference Rules\label{lp:ir}}
a <-- b \wedge c . b <-- d . b <-- e . ?- a .By applying the inference rules to the program we derive the following additional queries:
?- b \wedge c . ?- d \wedge c . ?- e \wedge c. ?- c. ?-Among the queries is an empty query. The presence of the empty query indicates that the original query is satisfiable, that is, the answer to the query is yes. Alternatively, the query is a theorem, provable from the given facts and rules.
A' can be deduced, if A' :- B'1,...,B'n is an instance of R.R = (A :- B1,...,Bn) and B'1. ... B'n.
Definition: A logic program is a finite set of rules.
Definition: An existentially quantified goal G is a logical consequence of a program P iff there is a clause in P with an instance A :- B1,...,Bn, n \geq 0 such that B1,...,Bn are logical consequences of P and A is an instance of G.
The control portion of the the equation is provide by an inference engine whose role is to derive theorems based on the set of axioms provided by the programmer. The inference engine uses the operations of resolution and unification to construct proofs.
Resolution says that given the axioms
the factf if a0, ..., a_m. g if f, b0, ..., bn.
can be derived.g if a0, ..., a_m, b0, ..., bn.
Unification is the binding of variables. For example
A query containing a variable asks whether there is a value for the variable that makes the query a logical consequence of the program.
Note that variables do not denote a specified storage location, but denote an unspecified but single entity.?- father(bill,X). ?- father(X,mary). ?- father(X,Y).
Definition: Constants and variables are terms. A compound term is comprised of a functor and a sequence of terms. A functor is characterized by its name, which is an atom and its arity, or number of arguments.
Definition: Queries, facts and terms which do not contain variables are called ground. Where variables do occur they are called nonground.X, 3, mary, fatherof(F,mary), ...
Definition: A substitution is a finite set (possibly empty) of pairs of the form Xi=ti, where Xi is a variable and ti is a term, and Xi\neq Xj for every i \neq j, and Xi does not occur in tj, for any i and j.
Definition: A is an instance of B if there is a substitution theta such that A = Btheta.p(a,X,t(Z)), p(Y,m,Q); theta = { X=m,Y=a,Q=t(Z) }
Definition: Two terms A and B are said to have a common instance C iff there are substitutions theta1 and theta2 such that C = Atheta1 and C = Btheta2.
Definition: A unifier of two terms A and B is a substitution making the two terms identical. If two terms have a unifer they are said to unify.A = plus(0,3,Y), B = plus(0,X,X). C = plus(0,3,3) since C = A{ Y=3} and C = B{ X=3}.
Definition: A most general unifier or mgu of two terms is a unifier such that the associated common instance is most general.p(a,X,t(Z))theta = p(Y,m,Q)theta where theta = { X=m,Y=a,Q=t(Z) }
unify(A,B) :- unify1(A,B).unify1(X,Y) :- X == Y.
unify1(X,Y) :- var(X), var(Y), X=Y. % The substitution
unify1(X,Y) :- var(X), nonvar(Y), \+ occurs(X,Y), X=Y. % The substitution
unify1(X,Y) :- var(Y), nonvar(X), \+ occurs(Y,X), Y=X. % The substitution
unify1(X,Y) :- nonvar(X), nonvar(Y), functor(X,F,N), functor(Y,F,N),
X =..[F|R], Y =..[F|T], match_list(R,T).match_list([],[]).
match_list([X|R],[H|T]) :- unify(X,H), match_list(R,T).occurs(A,B) :- A == B.
occurs(A,B) :- nonvar(B), functor(B,F,N), occurs(A,B,N).occurs(A,B,N) :- N > 0, arg(N,B,AN), occurs(A,AN),!. % RED
occurs(A,B,M) :- M > 0, N is M - 1, occurs(A,B,N).
Figure M.N: A simple interpreter for pure Prolog is_true( Goals ) <-- resolved( Goals ). is_true( Goals ) <-- write( no ), nl. resolved([]). resolved(Goals) <-- select(Goal,Goals,RestofGoals), % Goal unifies with head of some rule clause(Head,Body), unify( Goal, Head ), add(Body,RestofGoals,NewGoals), resolved(NewGoals). prove(true). prove((A,B)) <-- prove(A), prove(B). % select first goal prove(A) <-- clause(A,B), prove(B). % select only goal and find a rule
is the Prolog code for an interpreter.
The interpreter can be used as the starting point for the construction of a debugger for Prolog programs and a starting point for the construction of an inference engine for an expert system.
The operational semantics for Prolog are given in Figure~\ref{lp:opsem}
Logic Programming (Horn Clause Logic) -- Operational Semantics Abstract Syntax: P in Programs C in Clauses Q in Queries T in Terms A in Atoms X in Variables P ::= (C | Q)... C ::= G [ <-- G_{1} [ \wedge G_{2}]...] . G ::= A [ ( T [,T]... ) ] T ::= X | A [ ( T [,T]... ) ] Q ::= G [,G]... ? Semantic Domains: beta in {\bf B} = Bindings \epsilon in {\bf E} = Environment Semantic Functions: R in Q} --> B} --> B } + (B} × }yes }) + } no } } U in C } × C } --> B } --> B } Semantic Equations: R[ ?} ] beta , \epsilon &=& (beta, yes}) R[ G ] beta , \epsilon &=& beta' & where } &&G' in \epsilon , U [ G, G' ] beta = beta' R[ G ] beta , \epsilon &=& R[ B ] beta' , \epsilon & where } &&(G' <-- B) in \epsilon , U [ G, G' ] beta = beta' R[ G1,G2 ] beta , \epsilon &=& R[ B,G2 ] ( R [ G1 ] beta , \epsilon ), \epsilon R[ G } ] beta , \epsilon &=& no } & where no other rule applies} \caption{Operational semantics\label{lp:opsem}}
Definition M.N: Let P be a logic program. The Herbrand universe of P, denoted by U(P) is the set of ground terms that can be formed from the constants and function symbols appearing in P
Definition M.N: The Herbrand base, denoted by {\cal B}(P), is the set of all ground goals that can be formed from the predicates in P and the terms in the Herbrand universe.
The Herbrand base is infinite if the Herbrand universe is.
Definition M.N: An interpretation for a logic program is a subset of the Herbrand base.
An interpretation assigns truth and falsity to the elements of the Herbrand base. A goal in the Herbrand base is true with respect to an interpretation if it is a member of it, false otherwise.
Definition M.N: An interpretation I is a model for a logic program if for each ground instance of a clause in the program A <-- B1, ... , Bn A is in I if B1, ... , Bn are in I.
This approach to the semantics is often called the term model.
Here is a definition of the relation between the prefix and suffixes of a list. The relation is named concat because it may be viewed as defining the result of appending two lists to get the third list.
{l} concat([ ],[ ]) concat([H|T],L,[H|TL]) <-- concat(T,L,TL)
Logical variables operate in a way much different than variables in traditional programming languages. By way of illustration, consider the following instances of the concat relation.
The underscore _ designates an anonymous variable, it matches anything.concat(L1,L2,L) prefix(Pre,L) <-- concat(Pre,_,L). sufix(Suf,L) <-- concat(_,Suf,L). split(L,Pre,Suf) <-- concat(Pre,Suf,L). member(X,L) <-- concat(_,[X|_],L).
There two simple types of constants, string and numeric. Arrays may be represented as a relation. For example, the two dimensional matrix
may be written as {ll} data(1,1,mary)&data(1,2,18.47) data(2,1,john)&data(2,2,34.6) data(3,1,jane)&data(3,2,64.4)
Records may be represented as terms and the fields accessed through pattern matching.
Lists are written between brackets [ and ], so [ ] is the empty list and [b, c] is the list of two symbols b and c. If H is a symbol and T is a list then [H|T] is a list with head H and tail T. Stacks may then be represented as a list. Trees may be represented as lists of lists or as terms.book(author( last(aaby), first(anthony), mi(a)), title('programming language concepts), pub(wadsworth), date(1991)) book(A,T,pub(W),D)
Lists may be used to simulate stacks, queues and trees. In addition, the logical variable may be used to implement incomplete data structures.
This is a sequence of calls. Note that the initial call is with the variable BT.lookup(Key,Data,bt(Key,Data,LT,RT)) lookup(Key,Data,bt(Key0,Data0,LT,RT)) <-- Key @< Key0, lookup(Key,Data,LT) lookup(Key,Data,bt(Key0,Data0,LT,RT)) <-- Key @> Key0, lookup(Key,Data,RT)
The first three calls initialize the dictionary to contain those entries while the last call extracts janes's age from the dictionary.lookup(john,46,BT), lookup(jane,35,BT), lookup(allen,49,BT), lookup(jane,Age,BT).
The logical and the incomplete data structure can be used to append lists in constant time. The programming technique is known as difference lists. The empty difference list is X/X. The concat relation for difference lists is defined as follows:
Here is an example of a use of the definition.concat_dl(Xs/Ys, Ys/Zs, Xs/Zs)
The relation between ordinary lists and difference lists is defined as follows:?- concat_dl([1,2,3|X]/X,[4,5,6|Y]/Y,Z). _X = [4,5,6 | 11] _Y = 11 _Z = [1,2,3,4,5,6 | 11] / 11 Yes
ol_dl([ ],X/X) <-- var(X) ol_dl([F|R],[F|DL]/Y) <-- ol_dl(R,DL/Y)
However, the product is a pattern rather than a value. In order to force the evaluation of an expression, a Prolog definition of the same relation would be writtentimes(X,Y,X×Y)
times(X,Y,Z) <-- Z is X×Y
Here is a direct restatement of the definition in a relational form.n! = 1 if n = 0 n× (n-1)! if n > 0
In Prolog this definition does not evaluate either of the expressions N-1 or N×F thus the value 0 will not occur. To force evaluation of the expressions we rewrite the definition as follows.factorial(0,1) factorial(N,N×F) <-- factorial(N-1,F)
Note that in this last version, the call to the factorial predicate is not the last call on the right-hand side of the definition. When the last call on the right-hand side is a recursive call ({\it tail recursion}) then the definition is said to be an iterative definition. An iterative version of the factorial relation may be defined using an accumulator and tail recursion.factorial(0,1) factorial(N,F)<-- M is N-1, factorial(M,Fm), F is N×Fm
In this definition, there are two different fac relations, the first is a 2-ary relation, and the second is a 3-ary relation.fac(N,F) <-- fac(N,1,F) fac(0,F,F) fac(N,P,F) <-- NP is N×P, M is N-1, fac(M,NP,F)
As a further example of the relation between recursive and iterative definitions, here is a recursive version of the relation between a list and its reverse.
and here is an iterative version.reverse([ ],[ ]) reverse([H|T],R) <-- reverse(T,Tr), concat(Tr,[H],R)
Efficient implementation of recursion is possible when the recursion is tail recursion. Tail recursion is implementable as iteration provided no backtracking may be required (the only other predicate in the body are builtin predicates).rev(L,R)<-- rev(L,[ ],R) rev([ ],R,R) rev([H|T],L,R) <-- rev(T,[H|L],R)
If Aj and B_y `match' then by resolution: ...not Ai \/ not Aj \/ B_k...
...not A_x \/ B_y \/ B_z...
----------------------------
...not Ai \/...\/ A_x \/ B_z...\/ B_k
Matching is called unification.
Direction of Proof
Prolog is not logic programming. The execution model for Prolog omits the occurs check, searches the rules sequentially, and selects goals sequentially. Backtracking, infinite search trees ...
As in functional programming, lists are an important data structure logic programming. The empty list is represented by [ ], a list of n elements by [X1, ... , Xn] and the first i elements of a list and the rest of the list by [X1,...,Xi|R]. In addition, data structures of arbitrary complexity may be constructed from the terms.
The result is an infinite loop. The first and fourth clauses imply p( b, c ). The first and third clauses with the p( b, c) imply the query. Prolog gets lost in an infinite branch no matter how the clauses are ordered, how the literals in the bodies are ordered or what search rule with a fixed order for trying the clauses is used. Thus logical completeness requires a breadth-first search which is too inefficient to be practical.p( a, b ). p( c, b ). p( X, Z ) <-- p( X, Y ), p( Y, Z). p( X, Y ) <-- p( Y, X ). ?- p( a, c ).
Result is that X is always [ ]. Prologs depth-first search prevents it from finding other values.concat( [ ], L, L ). concat( [H|L1], L2, [X|L] ) <-- concat( L1, L2, L ). concat3( L1, L2, L3, L ) <-- concat( L1, L2, L12 ), concat( L12, L3 L ). ?- concat3( X, Y, [2], L).
Lacking the occur check Prolog will succeed but \verb+test+ is not a logical consequence of the logic program.test <-- p( X, X ). p( Y, f( Y )). ?- test.
The execution of this logic program results in the construction of an infinite data structure.
In this instance Prolog will succeed (with some trouble printing the answer). There are two solutions, the first is to change the logic and permit infinite terms, the second is to introduce the occur check with the resulting loss of efficiency.concat( [ ], L, L ). concat( [H|L1], L2, [X|L] ) <-- concat( L1, L2, L ). ?- concat( [ ], L, [1|L] ).
The goal succeeds but is not a logical consequence of the logic program.p( a ). r( b ) <-- not p( Y ). ?- not p(b).
The query is a logical consequence of the first two clauses but Prolog cannot determine that fact and enters an infinite derivation tree. However the closed world assumption is useful from a pragmatic point of view.q( a ) <-- r( a ). q( a ) <-- not r( a ). r( X ) <-- r( f( X ) ). ?- q ( a ).
a(1). a(2). a(3). p <-- a(I),!,print(I),nl,fail. ?- p. 1 No
Some other extralogical primitives include bagof, setof, assert, retract, univ. These are outside the scope of first-order logic.
Input and output introduce side effects.
The extralogical primitives \verb+bagof+, \verb+setof+, \verb+assert+, and \verb+retract+ are outside the scope of first-order logic but are useful from the pragmatic point of view.
In Prolog there are builtin predicates to test for the various syntactic types, lists, numbers, atoms, clauses. Some predicates which are commonly available are the following.
{ll} var(X)&X is a variable atomic(A)&A is an atom or a numeric constant functor(P,F,N)&P is an N-ary predicate with functor F clause(Head,Body)&Head <-- Body is a formula. L =..List, call(C), assert(C), retract(C), bagof(X,P,B), setof(X,P,B)
Figure M.N: trace(Q) <-- trace1([Q]) trace1([]) trace1([true|R]) <-- !, trace1(R). trace1([fail|R]) <-- !, print('< '), print(fail), nl, fail. trace1([B|R]) <-- B =..[','|BL], !, concat(BL,R,NR), trace1(NR). trace1([F|R]) <-- builtin(F), print('> '), print([F|R]), nl, F, trace1(R), print('< '), print(F), nl trace1([F|R]) <-- clause(F,B), print('> '), print([F|R]),nl, trace1([B|R]), print('< '), print(F), nl trace1([F|R]) <-- \+ builtin(F), \+ clause(F,B), print('> '), print([F|R]),nl, print('< '), print(F), print(' '), print(fail), nl, fail \caption{Program tracer for Prolog\label{lp:trace}}
contains an example of meta programming. The code implements a facility for tracing the execution of a Prolog program. To trace a Prolog program, instead of entering {\tt ?- P.} enter {\tt ?- trace(P).}
the query results in the sequence of answers.concat([ ],L,L). concat([H|T],L,[H|R]) <-- concat(T,L,R). ?- concat(L1,[2],L).
However, if the order of the rules defining $append$ are interchanged,L1 = [ ], L = [2] L1 = [V1], L = [V1,2] L1 = [V1,V2], L = [V1,V2,2] ...
then the execution fails to terminate, entering an infinite loop since the first rule is always applicable.append([H|T],L,[H|R]) :- append(T,L,R). append([ ],L,L). ?- append(L1,[2],L).
The fundamental entity in a relational database is a relation which is viewed as a table of rows and columns, where each row, called a tuple, is an object and each column is an attribute or propery of the object.
A database consists of one or more relations. The data stored in the relations is manipulated using commands written in a query language. The operations provided the query language include union, set difference, cartesian product, projection, and selection. The first-order predicate logic can be used to represent knowledge and as a language for expressing operations on relations. -- Ullman (Principles of Database and Knowledge-base Systems) CSP 1988.
union( variables ) :- first_relation( variables ).
union( variables ) :- second_relation( variables ).
nat_join( variables shared variables ) :- r_1(variables, shared variables), r2(variables, shared variables).
Objective:
A is the head. B1, B2, ..., Bn. is the body. Facts, rules, and queries are also called Horn clauses or just clauses. Facts are also called unit clauses.A :- B1, B2, ..., Bn.
How to read rules. For every X and Y, if X is the father of Y and Y is an ancestor of Z, X is the ancestor of Z. For every X and Y, X is the ancestor of Z, if X is the father of Y and Y is an ancestor of Z.ancestor(X,Y) :- father(X,Y). ancestor(X,Z) :- father(X,Y), ancestor(Y,Z).
Operationally, to solve a conjunctive query, a single substitution must be found applicable to each conjunct.father(bill,jim). father(jim,jane). ?- father(bill,Y), father(Y,jane).
For A1,...,An and theta A1theta,...,Antheta each is deducible.
Variables in the body of a rule are read as universally quantified out side the rule and read as existentially quantified inside the rule. For every X and Y, if X is the father of Y and Y is an ancestor of Z, X is the ancestor of Z. if there exist X and Y such that X is the father of Y and Y is an ancestor of Z, then X is the ancestor of Z.father(adam,X).
Operationally, to solve a conjunctive query, a single substitution must be found applicable to each conjunct.father(bill,jim). father(jim,jane). ?- father(bill,Y), father(Y,jane).
For A1,...,An and theta
A1theta,...,Antheta each is deducible.